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 , log : function ( level , message , error ) { 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  https * 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 ; param username param password param options param options . clientId - The active directory application client id.
 * See  https * for an example.
 *  param options . tokenAudience param options . domain param options . environment param options . tokenCache returns export async function withUsernamePasswordWithAuthResponse ( username , password , options ? ) { 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 ( ) ; let tenantList = await buildTenantList ( creds ) ; if ( tenantList . length === 0 && tokenResponse . tenantId ) { tenantList = [ tokenResponse . tenantId ] ; } const subscriptionList = await _getSubscriptions ( creds , tenantList , options . tokenAudience ) ; return { credentials : creds , subscriptions : subscriptionList } ; } param clientId - The active directory application client Id also known as the SPN (ServicePrincipal Name).
 * See  https * for an example.
 *  param secret param domain param options param options . tokenAudience param options . environment param options . tokenCache returns export async function withServicePrincipalSecretWithAuthResponse ( clientId , secret , domain , options ? ) { 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 } ; } param clientId - The active directory application client Id also known as the SPN (ServicePrincipal Name).
 * See  https * for an example.
 *  param certificateStringOrFilePath param domain param options param options . tokenAudience param options . environment param options . tokenCache returns export async function withServicePrincipalCertificateWithAuthResponse ( clientId , certificateStringOrFilePath , domain , options ? ) { 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 , filePath ) { 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 , envUrl ) { 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 ( ) ; } param options param options . filePath param options . subscriptionEnvVariableName param optionalCallback returns export async function withAuthFileWithAuthResponse ( options ? ) { 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 , credsObj = { } ; const optionsForSp = { } ; content = readFileSync ( filePath , { encoding : "utf8" } ) ; credsObj = JSON . parse ( content ) ; validateAuthFileContent ( credsObj , filePath ) ; if ( ! credsObj . managementEndpointUrl ) { credsObj . managementEndpointUrl = credsObj . resourceManagerEndpointUrl ; } process . env [ subscriptionEnvVariableName ] = credsObj . subscriptionId ; const envFound = { 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 { const envParams = { 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 ) ; } param options param options . clientId - The active directory application client id.
 * See  https * for an example.
 *
 *  param options . tokenAudience param options . domain param options . environment param options . tokenCache param options . language param options . userCodeResponseLogger param optionalCallback returns export async function withInteractiveWithAuthResponse ( options ? ) { 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 = { } ; 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 = interactiveOptions . environment . activeDirectoryEndpointUrl + interactiveOptions . domain ; const authContext = new adal . AuthenticationContext ( authorityUrl , interactiveOptions . environment . validateAuthority , interactiveOptions . tokenCache ) ; interactiveOptions . context = authContext ; function tryAcquireToken ( interactiveOptions , resolve , reject ) { authContext . acquireUserCode ( interactiveOptions . tokenAudience ! , interactiveOptions . clientId ! , interactiveOptions . language ! , ( err , userCodeRes ) => { 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 } ; } param options param options . filePath param options . subscriptionEnvVariableName param optionalCallback returns export function withAuthFile ( ) ; export function withAuthFile ( options ) ; export function withAuthFile ( options , callback ) ; export function withAuthFile ( callback ) ; export function withAuthFile ( options ? , callback ? ) { 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 , authRes ) => { if ( err ) { return cb ( err ) ; } return cb ( undefined , authRes . credentials , authRes . subscriptions ) ; } ) ; } } param options param options . clientId - The active directory application client id.
 * See  https * for an example.
 *  param options . tokenAudience param options . domain param options . environment param options . tokenCache param options . language param options . userCodeResponseLogger param optionalCallback returns export function interactive ( ) ; export function interactive ( options ) ; export function interactive ( options , callback ) ; export function interactive ( callback ) ; export function interactive ( options ? , callback ? ) { 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 , authRes ) => { if ( err ) { return cb ( err ) ; } return cb ( undefined , authRes . credentials , authRes . subscriptions ) ; } ) ; } } param clientId - The active directory application client Id also known as the SPN (ServicePrincipal Name).
 * See  https * for an example.
 *  param secret param domain param options param options . tokenAudience param options . environment param options . tokenCache param optionalCallback returns export function withServicePrincipalSecret ( clientId , secret , domain ) ; export function withServicePrincipalSecret ( clientId , secret , domain , options ) ; export function withServicePrincipalSecret ( clientId , secret , domain , options , callback ) ; export function withServicePrincipalSecret ( clientId , secret , domain , callback ) ; export function withServicePrincipalSecret ( clientId , secret , domain , options ? , callback ? ) { 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 , authRes ) => { if ( err ) { return cb ( err ) ; } return cb ( undefined , authRes . credentials , authRes . subscriptions ) ; } ) ; } } param clientId - The active directory application client Id also known as the SPN (ServicePrincipal Name).
 * See  https * for an example.
 *  param certificateStringOrFilePath param domain param options param options . tokenAudience param options . environment param options . tokenCache param optionalCallback returns export function withServicePrincipalCertificate ( clientId , certificateStringOrFilePath , domain ) ; export function withServicePrincipalCertificate ( clientId , certificateStringOrFilePath , domain , options ) ; export function withServicePrincipalCertificate ( clientId , certificateStringOrFilePath , domain , options , callback ) ; export function withServicePrincipalCertificate ( clientId , certificateStringOrFilePath , domain , callback ) ; export function withServicePrincipalCertificate ( clientId , certificateStringOrFilePath , domain , options ? , callback ? ) { 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 , authRes ) => { if ( err ) { return cb ( err ) ; } return cb ( undefined , authRes . credentials , authRes . subscriptions ) ; } ) ; } } param username param password param options param options . clientId - The active directory application client id.
 * See  https * for an example.
 *  param options . tokenAudience param options . domain param options . environment param options . tokenCache param optionalCallback returns export function withUsernamePassword ( username , password ) ; export function withUsernamePassword ( username , password , options ) ; export function withUsernamePassword ( username , password , callback ) ; export function withUsernamePassword ( username , password , options , callback ) ; export function withUsernamePassword ( username , password , options ? , callback ? ) { 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 , authRes ) => { 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 , tenants , tokenAudience ? ) { if ( tokenAudience && ! managementPlaneTokenAudiences . some ( ( item ) => { return item === tokenAudience ! . toLowerCase ( ) ; } ) ) { return Promise . resolve ( [ ] ) ; } return getSubscriptionsFromTenants ( creds , tenants ) ; } param domain param options param options . port param options . resource param options . aadEndpoint param callback async function _withMSI ( options ? ) { if ( ! options ) { options = { } ; } const creds = new MSIVmTokenCredentials ( options ) ; await creds . getToken ( ) ; return creds ; } param options param options . port param options . resource param optionalCallback returns export function loginWithVmMSI ( ) ; export function loginWithVmMSI ( options ) ; export function loginWithVmMSI ( options , callback ) ; export function loginWithVmMSI ( callback ) ; export function loginWithVmMSI ( options ? , callback ? ) { 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 , tokenRes ) => { if ( err ) { return cb ( err ) ; } return cb ( undefined , tokenRes ) ; } ) ; } } /**
 * Private method
 */ async function _withAppServiceMSI ( options ) { if ( ! options ) { options = { } ; } const creds = new MSIAppServiceTokenCredentials ( options ) ; await creds . getToken ( ) ; return creds ; } param options param options . msiEndpoint param options . msiSecret param options . resource param options . msiApiVersion param optionalCallback returns export function loginWithAppServiceMSI ( ) ; export function loginWithAppServiceMSI ( options ) ; export function loginWithAppServiceMSI ( options , callback ) ; export function loginWithAppServiceMSI ( callback ) ; export function loginWithAppServiceMSI ( options ? , callback ? ) { 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 , tokenRes ) => { if ( err ) { return cb ( err ) ; } return cb ( undefined , tokenRes ) ; } ) ; } } param cmdArguments export async function execAz ( cmdArguments ) { 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 ) ) ; } } ) ; } ) ; }