@azure/ms-rest-nodeauth
Version:
Azure Authentication library in node.js with type definitions.
1,204 lines (1,143 loc) • 49.7 kB
text/typescript
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import * as adal from "adal-node";
import * as msRest from "@azure/ms-rest-js";
import { execFile } from "child_process";
import { readFileSync } from "fs";
import { Environment } from "@azure/ms-rest-azure-env";
import { TokenCredentialsBase } from "./credentials/tokenCredentialsBase";
import { ApplicationTokenCredentials } from "./credentials/applicationTokenCredentials";
import { ApplicationTokenCertificateCredentials } from "./credentials/applicationTokenCertificateCredentials";
import { DeviceTokenCredentials } from "./credentials/deviceTokenCredentials";
import { UserTokenCredentials } from "./credentials/userTokenCredentials";
import { AuthConstants, TokenAudience } from "./util/authConstants";
import {
buildTenantList,
getSubscriptionsFromTenants,
LinkedSubscription
} from "./subscriptionManagement/subscriptionUtils";
import { MSIVmTokenCredentials, MSIVmOptions } from "./credentials/msiVmTokenCredentials";
import {
MSIAppServiceTokenCredentials,
MSIAppServiceOptions
} from "./credentials/msiAppServiceTokenCredentials";
import { MSITokenResponse } from "./credentials/msiTokenCredentials";
/**
* Urls for management plane token
* audience across different azure environments.
*/
const managementPlaneTokenAudiences = [
"https://management.core.windows.net/",
"https://management.core.chinacloudapi.cn/",
"https://management.core.usgovcloudapi.net/",
"https://management.core.cloudapi.de/",
"https://management.azure.com/",
"https://management.core.windows.net",
"https://management.core.chinacloudapi.cn",
"https://management.core.usgovcloudapi.net",
"https://management.core.cloudapi.de",
"https://management.azure.com"
];
function turnOnLogging() {
const log = adal.Logging;
log.setLoggingOptions({
level: 3, // Please use log.LOGGING_LEVEL.VERBOSE once AD TypeScript mappings are updated,
log: function (level: any, message: any, error: any) {
level;
console.info(message);
if (error) {
console.error(error);
}
}
});
}
if (process.env["AZURE_ADAL_LOGGING_ENABLED"]) {
turnOnLogging();
}
/**
* Describes optional parameters for servicePrincipal/secret authentication.
*/
export interface AzureTokenCredentialsOptions {
/**
* The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
* If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferably in a guid format).
*/
tokenAudience?: TokenAudience;
/**
* The Azure environment to authenticate with.
*/
environment?: Environment;
/**
* The token cache. Default value is MemoryCache from adal.
*/
tokenCache?: adal.TokenCache;
}
/**
* Describes optional parameters for username/password authentication.
*/
export interface LoginWithUsernamePasswordOptions extends AzureTokenCredentialsOptions {
/**
* The active directory application client id.
* See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
* for an example.
*/
clientId?: string;
/**
* The domain or tenant Id containing this application. Default value is "common".
*/
domain?: string;
}
/**
* Describes optional parameters for interactive authentication.
*/
export interface InteractiveLoginOptions extends LoginWithUsernamePasswordOptions {
/**
* A logger that logs the user code response message required for interactive login. When
* this option is specified the usercode response message will not be logged to console.
*/
userCodeResponseLogger?: any;
/**
* The language code specifying how the message should be localized to. Default value "en-us".
*/
language?: string;
}
/**
* Describes the authentication response.
*/
export interface AuthResponse<T extends TokenCredentialsBase = TokenCredentialsBase> {
/**
* The credentials object.
*/
credentials: T;
/**
* List of associated subscriptions. It will be empty for personal accounts, unless the login method is called with a tenant Id sent as the `domain` optional parameter.
*/
subscriptions?: LinkedSubscription[];
}
/**
* Describes optional parameters for login withAuthFile.
*/
export interface LoginWithAuthFileOptions {
/**
* Absolute file path to the auth file. If not provided
* then please set the environment variable AZURE_AUTH_LOCATION.
*/
filePath?: string;
/**
* The subscriptionId environment variable
* name. Default is "AZURE_SUBSCRIPTION_ID".
*/
subscriptionEnvVariableName?: string;
}
/**
* Generic callback type definition.
*
* The error occurred if any, while executing the request; otherwise undefined
* Result when call was successful.
*/
export type Callback<TResult> = (error?: Error, result?: TResult) => void;
/**
* Provides a UserTokenCredentials object and the list of subscriptions associated with that userId across all the applicable tenants.
* This method is applicable only for organizational ids that are not 2FA enabled otherwise please use interactive login.
*
* When using personal accounts, the `domain` property in the `options` parameter is required to be set to the Id of a tenant for that account. Otherwise, the resulting credential will not be able to access the account's resources.
*
* @param username - The user name for the Organization Id account.
* @param password - The password for the Organization Id account.
* @param options - Object representing optional parameters.
* @param options.clientId - The active directory application client id.
* See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
* for an example.
* @param options.tokenAudience - The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
* If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferably in a guid format).
* @param options.domain - The domain or tenant Id containing this application. Default value "common".
* @param options.environment - The azure environment to authenticate with.
* @param options.tokenCache - The token cache. Default value is the MemoryCache object from adal.
*
* @returns A Promise that resolves to AuthResponse, which contains `credentials` and an optional `subscriptions` array, and rejects with an Error.
*/
export async function withUsernamePasswordWithAuthResponse(
username: string,
password: string,
options?: LoginWithUsernamePasswordOptions
): Promise<AuthResponse<UserTokenCredentials>> {
if (!options) {
options = {};
}
if (!options.clientId) {
options.clientId = AuthConstants.DEFAULT_ADAL_CLIENT_ID;
}
if (!options.domain) {
options.domain = AuthConstants.AAD_COMMON_TENANT;
}
if (!options.environment) {
options.environment = Environment.AzureCloud;
}
const creds = new UserTokenCredentials(
options.clientId,
options.domain,
username,
password,
options.tokenAudience,
options.environment,
options.tokenCache
);
const tokenResponse = await creds.getToken();
// The token cache gets propulated for all the tenants as a part of building the tenantList.
let tenantList = await buildTenantList(creds);
if (tenantList.length === 0 && tokenResponse.tenantId) {
tenantList = [tokenResponse.tenantId];
}
const subscriptionList: LinkedSubscription[] = await _getSubscriptions(
creds,
tenantList,
options.tokenAudience
);
return { credentials: creds, subscriptions: subscriptionList };
}
/**
* Provides an ApplicationTokenCredentials object and the list of subscriptions associated with that servicePrincipalId/clientId across all the applicable tenants.
*
* When using personal accounts, the `domain` parameter is required to be set to the Id of a tenant for that account. Otherwise, the resulting credential will not be able to access the account's resources.
*
* @param clientId - The active directory application client Id also known as the SPN (ServicePrincipal Name).
* See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
* for an example.
* @param secret - The application secret for the service principal.
* @param domain - The domain or tenant Id containing this application.
* @param options - Object representing optional parameters.
* @param options.tokenAudience - The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
* If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferably in a guid format).
* @param options.environment - The azure environment to authenticate with.
* @param options.tokenCache - The token cache. Default value is the MemoryCache object from adal.
*
* @returns A Promise that resolves to AuthResponse, which contains "credentials" and optional "subscriptions" array and rejects with an Error.
*/
export async function withServicePrincipalSecretWithAuthResponse(
clientId: string,
secret: string,
domain: string,
options?: AzureTokenCredentialsOptions
): Promise<AuthResponse<ApplicationTokenCredentials>> {
if (!options) {
options = {};
}
if (!options.environment) {
options.environment = Environment.AzureCloud;
}
const creds = new ApplicationTokenCredentials(
clientId,
domain,
secret,
options.tokenAudience,
options.environment,
options.tokenCache
);
await creds.getToken();
const subscriptionList = await _getSubscriptions(creds, [domain], options.tokenAudience);
return { credentials: creds, subscriptions: subscriptionList };
}
/**
* Provides an ApplicationTokenCertificateCredentials object and the list of subscriptions associated with that servicePrincipalId/clientId across all the applicable tenants.
*
* When using personal accounts, the `domain` parameter is required to be set to the Id of a tenant for that account. Otherwise, the resulting credential will not be able to access the account's resources.
*
* @param clientId - The active directory application client Id also known as the SPN (ServicePrincipal Name).
* See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
* for an example.
* @param certificateStringOrFilePath - A PEM encoded certificate and private key OR an absolute filepath to the .pem file containing that information. For example:
* - CertificateString: "-----BEGIN PRIVATE KEY-----\n<xxxxx>\n-----END PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\n<yyyyy>\n-----END CERTIFICATE-----\n"
* - CertificateFilePath: **Absolute** file path of the .pem file.
* @param domain - The domain or tenant Id containing this application.
* @param options - Object representing optional parameters.
* @param options.tokenAudience - The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
* If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferably in a guid format).
* @param options.environment - The azure environment to authenticate with.
* @param options.tokenCache - The token cache. Default value is the MemoryCache object from adal.
*
* @returns A Promise that resolves to AuthResponse, which contains "credentials" and optional "subscriptions" array and rejects with an Error.
*/
export async function withServicePrincipalCertificateWithAuthResponse(
clientId: string,
certificateStringOrFilePath: string,
domain: string,
options?: AzureTokenCredentialsOptions
): Promise<AuthResponse<ApplicationTokenCertificateCredentials>> {
if (!options) {
options = {};
}
if (!options.environment) {
options.environment = Environment.AzureCloud;
}
const creds = ApplicationTokenCertificateCredentials.create(
clientId,
certificateStringOrFilePath,
domain,
options
);
await creds.getToken();
const subscriptionList = await _getSubscriptions(creds, [domain], options.tokenAudience);
return { credentials: creds, subscriptions: subscriptionList };
}
function validateAuthFileContent(credsObj: any, filePath: string) {
if (!credsObj) {
throw new Error("Please provide a credsObj to validate.");
}
if (!filePath) {
throw new Error("Please provide a filePath.");
}
if (!credsObj.clientId) {
throw new Error(`"clientId" is missing from the auth file: ${filePath}.`);
}
if (!credsObj.clientSecret && !credsObj.clientCertificate) {
throw new Error(
`Either "clientSecret" or "clientCertificate" must be present in the auth file: ${filePath}.`
);
}
if (!credsObj.subscriptionId) {
throw new Error(`"subscriptionId" is missing from the auth file: ${filePath}.`);
}
if (!credsObj.tenantId) {
throw new Error(`"tenantId" is missing from the auth file: ${filePath}.`);
}
if (!credsObj.activeDirectoryEndpointUrl) {
throw new Error(`"activeDirectoryEndpointUrl" is missing from the auth file: ${filePath}.`);
}
if (!credsObj.resourceManagerEndpointUrl) {
throw new Error(`"resourceManagerEndpointUrl" is missing from the auth file: ${filePath}.`);
}
if (!credsObj.activeDirectoryGraphResourceId) {
throw new Error(`"activeDirectoryGraphResourceId" is missing from the auth file: ${filePath}.`);
}
if (!credsObj.sqlManagementEndpointUrl) {
throw new Error(`"sqlManagementEndpointUrl" is missing from the auth file: ${filePath}.`);
}
}
function foundManagementEndpointUrl(authFileUrl: string, envUrl: string): boolean {
if (!authFileUrl || (authFileUrl && typeof authFileUrl.valueOf() !== "string")) {
throw new Error("authFileUrl cannot be null or undefined and must be of type string.");
}
if (!envUrl || (envUrl && typeof envUrl.valueOf() !== "string")) {
throw new Error("envUrl cannot be null or undefined and must be of type string.");
}
authFileUrl = authFileUrl.endsWith("/") ? authFileUrl.slice(0, -1) : authFileUrl;
envUrl = envUrl.endsWith("/") ? envUrl.slice(0, -1) : envUrl;
return authFileUrl.toLowerCase() === envUrl.toLowerCase();
}
/**
* Before using this method please install az cli from https://github.com/Azure/azure-cli/releases. Then execute `az ad sp create-for-rbac --sdk-auth > ${yourFilename.json}`.
* If you want to create the sp for a different cloud/environment then please execute:
* 1. az cloud list
* 2. az cloud set –n <name of the environment>
* 3. az ad sp create-for-rbac --sdk-auth > auth.json // create sp with secret
* **OR**
* 3. az ad sp create-for-rbac --create-cert --sdk-auth > auth.json // create sp with certificate
* If the service principal is already created then login with service principal info:
* 4. az login --service-principal -u <clientId> -p <clientSecret> -t <tenantId>
* 5. az account show --sdk-auth > auth.json
*
* Authenticates using the service principal information provided in the auth file. This method will set
* the subscriptionId from the auth file to the user provided environment variable in the options
* parameter or the default "AZURE_SUBSCRIPTION_ID".
*
* @param options - Optional parameters
* @param options.filePath - Absolute file path to the auth file. If not provided
* then please set the environment variable AZURE_AUTH_LOCATION.
* @param options.subscriptionEnvVariableName - The subscriptionId environment variable
* name. Default is "AZURE_SUBSCRIPTION_ID".
* @param optionalCallback - The optional callback.
*
* @returns A Promise that resolves to AuthResponse, which contains "credentials" and optional "subscriptions" array and rejects with an Error.
*/
export async function withAuthFileWithAuthResponse(
options?: LoginWithAuthFileOptions
): Promise<AuthResponse<ApplicationTokenCredentials | ApplicationTokenCertificateCredentials>> {
if (!options) options = { filePath: "" };
const filePath = options.filePath || process.env[AuthConstants.AZURE_AUTH_LOCATION];
const subscriptionEnvVariableName =
options.subscriptionEnvVariableName || "AZURE_SUBSCRIPTION_ID";
if (!filePath) {
const msg = `Either provide an absolute file path to the auth file or set/export the environment variable - ${AuthConstants.AZURE_AUTH_LOCATION}.`;
throw new Error(msg);
}
let content: string,
credsObj: any = {};
const optionsForSp: any = {};
content = readFileSync(filePath, { encoding: "utf8" });
credsObj = JSON.parse(content);
validateAuthFileContent(credsObj, filePath);
if (!credsObj.managementEndpointUrl) {
credsObj.managementEndpointUrl = credsObj.resourceManagerEndpointUrl;
}
// setting the subscriptionId from auth file to the environment variable
process.env[subscriptionEnvVariableName] = credsObj.subscriptionId;
// get the AzureEnvironment or create a new AzureEnvironment based on the info provided in the auth file
const envFound: any = {
name: ""
};
const envNames = Object.keys(Environment);
for (let i = 0; i < envNames.length; i++) {
const env = envNames[i];
const environmentObj = (Environment as any)[env];
if (
environmentObj &&
environmentObj.managementEndpointUrl &&
foundManagementEndpointUrl(
credsObj.managementEndpointUrl,
environmentObj.managementEndpointUrl
)
) {
envFound.name = environmentObj.name;
break;
}
}
if (envFound.name) {
optionsForSp.environment = (Environment as any)[envFound.name];
} else {
// create a new environment with provided info.
const envParams: any = {
// try to find a logical name or set the filepath as the env name.
name: credsObj.managementEndpointUrl.match(/.*management\.core\.(.*)\..*/i)[1] || filePath
};
const keys = Object.keys(credsObj);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (
key.match(/^(clientId|clientSecret|clientCertificate|subscriptionId|tenantId)$/gi) === null
) {
if (key === "activeDirectoryEndpointUrl" && !key.endsWith("/")) {
envParams[key] = credsObj[key] + "/";
} else {
envParams[key] = credsObj[key];
}
}
}
if (!envParams.activeDirectoryResourceId) {
envParams.activeDirectoryResourceId = credsObj.managementEndpointUrl;
}
if (!envParams.portalUrl) {
envParams.portalUrl = "https://portal.azure.com";
}
optionsForSp.environment = Environment.add(envParams);
}
if (credsObj.clientSecret) {
return withServicePrincipalSecretWithAuthResponse(
credsObj.clientId,
credsObj.clientSecret,
credsObj.tenantId,
optionsForSp
);
}
return withServicePrincipalCertificateWithAuthResponse(
credsObj.clientId,
credsObj.clientCertificate,
credsObj.tenantId,
optionsForSp
);
}
/**
* Provides a url and code that needs to be copy and pasted in a browser and authenticated over there. If successful, the user will get a DeviceTokenCredentials object and the list of subscriptions associated with that userId across all the applicable tenants.
*
* When using personal accounts, the `domain` property in the `options` parameter is required to be set to the Id of a tenant for that account. Otherwise, the resulting credential will not be able to access the account's resources.
*
* @param options - Object representing optional parameters.
*
* @param options.clientId - The active directory application client id.
* See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
* for an example.
*
* @param options.tokenAudience - The audience for which the token is requested. Valid value is "graph".If tokenAudience is provided
* then domain should also be provided its value should not be the default "common" tenant. It must be a string (preferably in a guid format).
*
* @param options.domain - The domain or tenant Id containing this application. Default value is "common".
*
* @param options.environment - The azure environment to authenticate with. Default environment is "Public Azure".
*
* @param options.tokenCache - The token cache. Default value is the MemoryCache object from adal.
*
* @param options.language - The language code specifying how the message should be localized to. Default value "en-us".
*
* @param options.userCodeResponseLogger - A logger that logs the user code response message required for interactive login. When
* this option is specified the usercode response message will not be logged to console.
*
* @param optionalCallback - The optional callback.
*
* @returns A Promise that resolves to AuthResponse, which contains "credentials" and optional "subscriptions" array and rejects with an Error.
*/
export async function withInteractiveWithAuthResponse(
options?: InteractiveLoginOptions
): Promise<AuthResponse<DeviceTokenCredentials>> {
if (!options) {
options = {};
}
if (!options.environment) {
options.environment = Environment.AzureCloud;
}
if (!options.domain) {
options.domain = AuthConstants.AAD_COMMON_TENANT;
}
if (!options.clientId) {
options.clientId = AuthConstants.DEFAULT_ADAL_CLIENT_ID;
}
if (!options.tokenCache) {
options.tokenCache = new adal.MemoryCache();
}
if (!options.language) {
options.language = AuthConstants.DEFAULT_LANGUAGE;
}
if (!options.tokenAudience) {
options.tokenAudience = options.environment.activeDirectoryResourceId;
}
const interactiveOptions: any = {};
interactiveOptions.tokenAudience = options.tokenAudience;
interactiveOptions.environment = options.environment;
interactiveOptions.domain = options.domain;
interactiveOptions.clientId = options.clientId;
interactiveOptions.tokenCache = options.tokenCache;
interactiveOptions.language = options.language;
interactiveOptions.userCodeResponseLogger = options.userCodeResponseLogger;
const authorityUrl: string =
interactiveOptions.environment.activeDirectoryEndpointUrl + interactiveOptions.domain;
const authContext = new adal.AuthenticationContext(
authorityUrl,
interactiveOptions.environment.validateAuthority,
interactiveOptions.tokenCache
);
interactiveOptions.context = authContext;
function tryAcquireToken(interactiveOptions: InteractiveLoginOptions, resolve: any, reject: any) {
authContext.acquireUserCode(
interactiveOptions.tokenAudience!,
interactiveOptions.clientId!,
interactiveOptions.language!,
(err: any, userCodeRes: adal.UserCodeInfo) => {
if (err) {
if (err.error === "authorization_pending") {
setTimeout(() => {
tryAcquireToken(interactiveOptions, resolve, reject);
}, 1000);
} else {
reject(err);
}
return;
}
if (interactiveOptions.userCodeResponseLogger) {
interactiveOptions.userCodeResponseLogger(userCodeRes.message);
} else {
console.log(userCodeRes.message);
}
return resolve(userCodeRes);
}
);
}
const getUserCode = new Promise<adal.UserCodeInfo>((resolve, reject) => {
return tryAcquireToken(interactiveOptions, resolve, reject);
});
const userCodeResponse = await getUserCode;
const creds = await new Promise<DeviceTokenCredentials>((resolve, reject) => {
return authContext.acquireTokenWithDeviceCode(
interactiveOptions.tokenAudience,
interactiveOptions.clientId,
userCodeResponse,
(error, tokenResponse) => {
if (error) {
return reject(error);
}
const response = tokenResponse as adal.TokenResponse;
interactiveOptions.userName = response.userId;
interactiveOptions.authorizationScheme = response.tokenType;
let creds;
try {
creds = new DeviceTokenCredentials(
interactiveOptions.clientId,
interactiveOptions.domain,
interactiveOptions.userName,
interactiveOptions.tokenAudience,
interactiveOptions.environment,
interactiveOptions.tokenCache
);
} catch (err) {
return reject(err);
}
return resolve(creds);
}
);
});
const tenants = await buildTenantList(creds);
const subscriptions = await _getSubscriptions(creds, tenants, interactiveOptions.tokenAudience);
return { credentials: creds, subscriptions: subscriptions };
}
/**
* Before using this method please install az cli from https://github.com/Azure/azure-cli/releases. Then execute `az ad sp create-for-rbac --sdk-auth > ${yourFilename.json}`.
* If you want to create the sp for a different cloud/environment then please execute:
* 1. az cloud list
* 2. az cloud set –n <name of the environment>
* 3. az ad sp create-for-rbac --sdk-auth > auth.json // create sp with secret
* **OR**
* 3. az ad sp create-for-rbac --create-cert --sdk-auth > auth.json // create sp with certificate
* If the service principal is already created then login with service principal info:
* 4. az login --service-principal -u <clientId> -p <clientSecret> -t <tenantId>
* 5. az account show --sdk-auth > auth.json
*
* Authenticates using the service principal information provided in the auth file. This method will set
* the subscriptionId from the auth file to the user provided environment variable in the options
* parameter or the default "AZURE_SUBSCRIPTION_ID".
*
* @param options - Optional parameters
* @param options.filePath - Absolute file path to the auth file. If not provided
* then please set the environment variable AZURE_AUTH_LOCATION.
* @param options.subscriptionEnvVariableName - The subscriptionId environment variable
* name. Default is "AZURE_SUBSCRIPTION_ID".
* @param optionalCallback - The optional callback.
*
* @returns If a callback was passed as the last parameter then it returns void else returns a Promise.
* The callback is called with the resulting ApplicationTokenCredentials or ApplicationTokenCertificateCredentials
* object and a list of associated subscriptions across all the applicable tenants.
*/
export function withAuthFile(): Promise<
ApplicationTokenCredentials | ApplicationTokenCertificateCredentials
>;
export function withAuthFile(
options: LoginWithAuthFileOptions
): Promise<ApplicationTokenCredentials | ApplicationTokenCertificateCredentials>;
export function withAuthFile(
options: LoginWithAuthFileOptions,
callback: {
(
err: Error,
credentials: ApplicationTokenCredentials | ApplicationTokenCertificateCredentials,
subscriptions: Array<LinkedSubscription>
): void;
}
): void;
export function withAuthFile(callback: any): void;
export function withAuthFile(
options?: LoginWithAuthFileOptions,
callback?: {
(
err: Error,
credentials: ApplicationTokenCredentials | ApplicationTokenCertificateCredentials,
subscriptions: Array<LinkedSubscription>
): void;
}
): any {
if (!callback && typeof options === "function") {
callback = options;
options = undefined;
}
const cb = callback as Function;
if (!callback) {
return withAuthFileWithAuthResponse(options).then((authRes) => {
return authRes.credentials;
});
} else {
msRest.promiseToCallback(withAuthFileWithAuthResponse(options))(
(
err: Error,
authRes: AuthResponse<ApplicationTokenCredentials | ApplicationTokenCertificateCredentials>
) => {
if (err) {
return cb(err);
}
return cb(undefined, authRes.credentials, authRes.subscriptions);
}
);
}
}
/**
* Provides a url and code that needs to be copy and pasted in a browser and authenticated over there. If successful, the user will get a DeviceTokenCredentials object and the list of subscriptions associated with that userId across all the applicable tenants.
*
* When using personal accounts, the `domain` property in the `options` parameter is required to be set to the Id of a tenant for that account. Otherwise, the resulting credential will not be able to access the account's resources.
*
* @param options - Object representing optional parameters.
* @param options.clientId - The active directory application client id.
* See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
* for an example.
* @param options.tokenAudience - The audience for which the token is requested. Valid value is "graph".If tokenAudience is provided
* then domain should also be provided its value should not be the default "common" tenant. It must be a string (preferably in a guid format).
* @param options.domain - The domain or tenant Id containing this application. Default value is "common".
* @param options.environment - The azure environment to authenticate with. Default environment is "Public Azure".
* @param options.tokenCache - The token cache. Default value is the MemoryCache object from adal.
* @param options.language - The language code specifying how the message should be localized to. Default value "en-us".
* @param options.userCodeResponseLogger - A logger that logs the user code response message required for interactive login. When
* this option is specified the usercode response message will not be logged to console.
* @param optionalCallback - The optional callback.
*
* @returns If a callback was passed as the last parameter then it returns void else returns a Promise.
* The callback is called with the resulting DeviceTokenCredentials object and a list of
* associated subscriptions across all the applicable tenants.
*/
export function interactive(): Promise<DeviceTokenCredentials>;
export function interactive(options: InteractiveLoginOptions): Promise<DeviceTokenCredentials>;
export function interactive(
options: InteractiveLoginOptions,
callback: {
(
err: Error,
credentials: DeviceTokenCredentials,
subscriptions: Array<LinkedSubscription>
): void;
}
): void;
export function interactive(callback: any): void;
export function interactive(
options?: InteractiveLoginOptions,
callback?: {
(
err: Error,
credentials: DeviceTokenCredentials,
subscriptions: Array<LinkedSubscription>
): void;
}
): any {
if (!callback && typeof options === "function") {
callback = options;
options = undefined;
}
const cb = callback as Function;
if (!callback) {
return withInteractiveWithAuthResponse(options).then((authRes) => {
return authRes.credentials;
});
} else {
msRest.promiseToCallback(withInteractiveWithAuthResponse(options))(
(err: Error, authRes: AuthResponse<DeviceTokenCredentials>) => {
if (err) {
return cb(err);
}
return cb(undefined, authRes.credentials, authRes.subscriptions);
}
);
}
}
/**
* Provides an ApplicationTokenCredentials object and the list of subscriptions associated with that servicePrincipalId/clientId across all the applicable tenants.
*
* When using personal accounts, the `domain` parameter is required to be set to the Id of a tenant for that account. Otherwise, the resulting credential will not be able to access the account's resources.
*
* @param clientId - The active directory application client Id also known as the SPN (ServicePrincipal Name).
* See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
* for an example.
* @param secret - The application secret for the service principal.
* @param domain - The domain or tenant Id containing this application.
* @param options - Object representing optional parameters.
* @param options.tokenAudience - The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
* If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferably in a guid format).
* @param options.environment - The azure environment to authenticate with.
* @param options.tokenCache - The token cache. Default value is the MemoryCache object from adal.
* @param optionalCallback - The optional callback.
*
* @returns If a callback was passed as the last parameter then it returns void else returns a Promise.
* The callback is called with the resulting ApplicationTokenCredentials object and a list of
* associated subscriptions across all the applicable tenants.
*/
export function withServicePrincipalSecret(
clientId: string,
secret: string,
domain: string
): Promise<ApplicationTokenCredentials>;
export function withServicePrincipalSecret(
clientId: string,
secret: string,
domain: string,
options: AzureTokenCredentialsOptions
): Promise<ApplicationTokenCredentials>;
export function withServicePrincipalSecret(
clientId: string,
secret: string,
domain: string,
options: AzureTokenCredentialsOptions,
callback: {
(
err: Error,
credentials: ApplicationTokenCredentials,
subscriptions: Array<LinkedSubscription>
): void;
}
): void;
export function withServicePrincipalSecret(
clientId: string,
secret: string,
domain: string,
callback: any
): void;
export function withServicePrincipalSecret(
clientId: string,
secret: string,
domain: string,
options?: AzureTokenCredentialsOptions,
callback?: {
(
err: Error,
credentials: ApplicationTokenCredentials,
subscriptions: Array<LinkedSubscription>
): void;
}
): any {
if (!callback && typeof options === "function") {
callback = options;
options = undefined;
}
const cb = callback as Function;
if (!callback) {
return withServicePrincipalSecretWithAuthResponse(clientId, secret, domain, options).then(
(authRes) => {
return authRes.credentials;
}
);
} else {
msRest.promiseToCallback(
withServicePrincipalSecretWithAuthResponse(clientId, secret, domain, options)
)((err: Error, authRes: AuthResponse) => {
if (err) {
return cb(err);
}
return cb(undefined, authRes.credentials, authRes.subscriptions);
});
}
}
/**
* Provides an ApplicationTokenCertificateCredentials object and the list of subscriptions associated with that servicePrincipalId/clientId across all the applicable tenants.
*
* When using personal accounts, the `domain` parameter is required to be set to the Id of a tenant for that account. Otherwise, the resulting credential will not be able to access the account's resources.
*
* @param clientId - The active directory application client Id also known as the SPN (ServicePrincipal Name).
* See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
* for an example.
* @param certificateStringOrFilePath - A PEM encoded certificate and private key OR an absolute filepath to the .pem file containing that information. For example:
* - CertificateString: "-----BEGIN PRIVATE KEY-----\n<xxxxx>\n-----END PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\n<yyyyy>\n-----END CERTIFICATE-----\n"
* - CertificateFilePath: **Absolute** file path of the .pem file.
* @param domain - The domain or tenant Id containing this application.
* @param options - Object representing optional parameters.
* @param options.tokenAudience - The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
* If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferably in a guid format).
* @param options.environment - The azure environment to authenticate with.
* @param options.tokenCache - The token cache. Default value is the MemoryCache object from adal.
* @param optionalCallback - The optional callback.
*
* @returns If a callback was passed as the last parameter then it returns void else returns a Promise.
* The callback is called with the resulting ApplicationTokenCertificateCredentials object and a list of
* associated subscriptions across all the applicable tenants.
*/
export function withServicePrincipalCertificate(
clientId: string,
certificateStringOrFilePath: string,
domain: string
): Promise<ApplicationTokenCertificateCredentials>;
export function withServicePrincipalCertificate(
clientId: string,
certificateStringOrFilePath: string,
domain: string,
options: AzureTokenCredentialsOptions
): Promise<ApplicationTokenCertificateCredentials>;
export function withServicePrincipalCertificate(
clientId: string,
certificateStringOrFilePath: string,
domain: string,
options: AzureTokenCredentialsOptions,
callback: {
(
err: Error,
credentials: ApplicationTokenCertificateCredentials,
subscriptions: Array<LinkedSubscription>
): void;
}
): void;
export function withServicePrincipalCertificate(
clientId: string,
certificateStringOrFilePath: string,
domain: string,
callback: any
): void;
export function withServicePrincipalCertificate(
clientId: string,
certificateStringOrFilePath: string,
domain: string,
options?: AzureTokenCredentialsOptions,
callback?: {
(
err: Error,
credentials: ApplicationTokenCertificateCredentials,
subscriptions: Array<LinkedSubscription>
): void;
}
): any {
if (!callback && typeof options === "function") {
callback = options;
options = undefined;
}
const cb = callback as Function;
if (!callback) {
return withServicePrincipalCertificateWithAuthResponse(
clientId,
certificateStringOrFilePath,
domain,
options
).then((authRes) => {
return authRes.credentials;
});
} else {
msRest.promiseToCallback(
withServicePrincipalCertificateWithAuthResponse(
clientId,
certificateStringOrFilePath,
domain,
options
)
)((err: Error, authRes: AuthResponse) => {
if (err) {
return cb(err);
}
return cb(undefined, authRes.credentials, authRes.subscriptions);
});
}
}
/**
* Provides a UserTokenCredentials object and the list of subscriptions associated with that userId across all the applicable tenants.
*
* This method is applicable only for organizational ids that are not 2FA enabled otherwise please use interactive login.
*
* When using personal accounts, the `domain` property in the `options` parameter is required to be set to the Id of a tenant for that account. Otherwise, the resulting credential will not be able to access the account's resources.
*
* @param username - The user name for the Organization Id account.
* @param password - The password for the Organization Id account.
* @param options - Object representing optional parameters.
* @param options.clientId - The active directory application client id.
* See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
* for an example.
* @param options.tokenAudience - The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
* If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferably in a guid format).
* @param options.domain - The domain or tenant Id containing this application. Default value "common".
* @param options.environment - The azure environment to authenticate with.
* @param options.tokenCache - The token cache. Default value is the MemoryCache object from adal.
* @param optionalCallback - The optional callback.
*
* @returns If a callback was passed as the last parameter then it returns void else returns a Promise.
* The callback is called with the resulting UserTokenCredentials object and a list of
* associated subscriptions across all the applicable tenants.
*/
export function withUsernamePassword(
username: string,
password: string
): Promise<UserTokenCredentials>;
export function withUsernamePassword(
username: string,
password: string,
options: LoginWithUsernamePasswordOptions
): Promise<UserTokenCredentials>;
export function withUsernamePassword(username: string, password: string, callback: any): void;
export function withUsernamePassword(
username: string,
password: string,
options: LoginWithUsernamePasswordOptions,
callback: {
(err: Error, credentials: UserTokenCredentials, subscriptions: Array<LinkedSubscription>): void;
}
): void;
export function withUsernamePassword(
username: string,
password: string,
options?: LoginWithUsernamePasswordOptions,
callback?: {
(err: Error, credentials: UserTokenCredentials, subscriptions: Array<LinkedSubscription>): void;
}
): any {
if (!callback && typeof options === "function") {
callback = options;
options = undefined;
}
const cb = callback as Function;
if (!callback) {
return withUsernamePasswordWithAuthResponse(username, password, options).then((authRes) => {
return authRes.credentials;
});
} else {
msRest.promiseToCallback(withUsernamePasswordWithAuthResponse(username, password, options))(
(err: Error, authRes: AuthResponse) => {
if (err) {
return cb(err);
}
return cb(undefined, authRes.credentials, authRes.subscriptions);
}
);
}
}
/**
* We only need to get the subscription list if the tokenAudience is for a management client.
*/
function _getSubscriptions(
creds: TokenCredentialsBase,
tenants: string[],
tokenAudience?: string
): Promise<LinkedSubscription[]> {
if (
tokenAudience &&
!managementPlaneTokenAudiences.some((item) => {
return item === tokenAudience!.toLowerCase();
})
) {
return Promise.resolve([]);
}
return getSubscriptionsFromTenants(creds, tenants);
}
/**
* Initializes MSITokenCredentials class and calls getToken and returns a token response.
*
* @param domain - - required. The tenant id.
* @param options - - Optional parameters
* @param options.port - port on which the MSI service is running on the host VM. Default port is 50342
* @param options.resource - The resource uri or token audience for which the token is needed. Default - "https://management.azure.com/"
* @param options.aadEndpoint - The add endpoint for authentication. default - "https://login.microsoftonline.com"
* @param callback - - the callback function.
*/
async function _withMSI(options?: MSIVmOptions): Promise<MSIVmTokenCredentials> {
if (!options) {
options = {};
}
const creds = new MSIVmTokenCredentials(options);
await creds.getToken();
return creds;
}
/**
* Before using this method please install az cli from https://github.com/Azure/azure-cli/releases.
* If you have an Azure virtual machine provisioned with az cli and has MSI enabled,
* you can then use this method to get auth tokens from the VM.
*
* To create a new VM, enable MSI, please execute this command:
* az vm create -g <resource_group_name> -n <vm_name> --assign-identity --image <os_image_name>
* Note: the above command enables a service endpoint on the host, with a default port 50342
*
* To enable MSI on a already provisioned VM, execute the following command:
* az vm --assign-identity -g <resource_group_name> -n <vm_name> --port <custom_port_number>
*
* To know more about this command, please execute:
* az vm --assign-identity -h
*
* Authenticates using the identity service running on an Azure virtual machine.
* This method makes a request to the authentication service hosted on the VM
* and gets back an access token.
*
* @param options - Optional parameters
* @param options.port - port on which the MSI service is running on the host VM. Default port is 50342
* @param options.resource - The resource uri or token audience for which the token is needed.
* For e.g. it can be:
* - resourcemanagement endpoint "https://management.azure.com/"(default)
* - management endpoint "https://management.core.windows.net/"
* @param optionalCallback - The optional callback.
*
* @returns If a callback was passed as the last parameter then it returns void else returns a Promise.
* The callback is called with the resulting MSIVmTokenCredentials object.
*/
export function loginWithVmMSI(): Promise<MSIVmTokenCredentials>;
export function loginWithVmMSI(options: MSIVmOptions): Promise<MSIVmTokenCredentials>;
export function loginWithVmMSI(
options: MSIVmOptions,
callback: Callback<MSIVmTokenCredentials>
): void;
export function loginWithVmMSI(callback: Callback<MSIVmTokenCredentials>): void;
export function loginWithVmMSI(
options?: MSIVmOptions | Callback<MSIVmTokenCredentials>,
callback?: Callback<MSIVmTokenCredentials>
): void | Promise<MSIVmTokenCredentials> {
if (!callback && typeof options === "function") {
callback = options;
options = {};
}
const cb = callback as Function;
if (!callback) {
return _withMSI(options as MSIVmOptions);
} else {
msRest.promiseToCallback(_withMSI(options as MSIVmOptions))(
(err: Error, tokenRes: MSITokenResponse) => {
if (err) {
return cb(err);
}
return cb(undefined, tokenRes);
}
);
}
}
/**
* Private method
*/
async function _withAppServiceMSI(
options: MSIAppServiceOptions
): Promise<MSIAppServiceTokenCredentials> {
if (!options) {
options = {};
}
const creds = new MSIAppServiceTokenCredentials(options);
await creds.getToken();
return creds;
}
/**
* Authenticate using the App Service MSI.
* @param options - Optional parameters
* @param options.msiEndpoint - The local URL from which your app can request tokens.
* Either provide this parameter or set the environment variable `MSI_ENDPOINT`.
* For example: `MSI_ENDPOINT="http://127.0.0.1:41741/MSI/token/"`
* @param options.msiSecret - The secret used in communication between your code and the local MSI agent.
* Either provide this parameter or set the environment variable `MSI_SECRET`.
* For example: `MSI_SECRET="69418689F1E342DD946CB82994CDA3CB"`
* @param options.resource - The resource uri or token audience for which the token is needed.
* For example, it can be:
* - resourcemanagement endpoint "https://management.azure.com/"(default)
* - management endpoint "https://management.core.windows.net/"
* @param options.msiApiVersion - The api-version of the local MSI agent. Default value is "2017-09-01".
* @param optionalCallback - The optional callback.
* @returns If a callback was passed as the last parameter then it returns void else returns a Promise.
* The callback is called with the resulting MSIAppServiceTokenCredentials object.
*/
export function loginWithAppServiceMSI(): Promise<MSIAppServiceTokenCredentials>;
export function loginWithAppServiceMSI(
options: MSIAppServiceOptions
): Promise<MSIAppServiceTokenCredentials>;
export function loginWithAppServiceMSI(
options: MSIAppServiceOptions,
callback: Callback<MSIAppServiceTokenCredentials>
): void;
export function loginWithAppServiceMSI(callback: Callback<MSIAppServiceTokenCredentials>): void;
export function loginWithAppServiceMSI(
options?: MSIAppServiceOptions | Callback<MSIAppServiceTokenCredentials>,
callback?: Callback<MSIAppServiceTokenCredentials>
): void | Promise<MSIAppServiceTokenCredentials> {
if (!callback && typeof options === "function") {
callback = options;
options = {};
}
const cb = callback as Function;
if (!callback) {
return _withAppServiceMSI(options as MSIAppServiceOptions);
} else {
msRest.promiseToCallback(_withAppServiceMSI(options as MSIAppServiceOptions))(
(err: Error, tokenRes: MSITokenResponse) => {
if (err) {
return cb(err);
}
return cb(undefined, tokenRes);
}
);
}
}
/**
* Executes the azure cli command and returns the result. It will be `undefined` if the command did
* not return anything or a `JSON object` if the command did return something.
* @param cmdArguments Arguments to the az cli command to execute.
*/
export async function execAz(cmdArguments: string[]): Promise<any> {
const azCmd = process.platform === "win32" ? "az.cmd" : "az";
return new Promise<any>((resolve, reject) => {
execFile(azCmd, [...cmdArguments, "--out", "json"], { encoding: "utf8" }, (error, stdout) => {
if (error) {
return reject(error);
}
try {
return resolve(JSON.parse(stdout));
} catch (err) {
const msg =
`An error occurred while parsing the output "${stdout}", of ` +
`the cmd az "${cmdArguments}": ${err.stack}.`;
return reject(new Error(msg));
}
});
});
}