diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..a9e8bc2 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +typings/ +**/*.d.ts diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..4b79ff3 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "arrowParens": "always", + "bracketSpacing": true, + "endOfLine": "lf", + "printWidth": 100, + "semi": true, + "singleQuote": false, + "tabWidth": 2, + "trailingComma": "none" +} \ No newline at end of file diff --git a/.scripts/checkPackageJsonVersion.ts b/.scripts/checkPackageJsonVersion.ts index ceb8d9b..3840e90 100644 --- a/.scripts/checkPackageJsonVersion.ts +++ b/.scripts/checkPackageJsonVersion.ts @@ -1,3 +1,3 @@ import { checkPackageJsonVersion } from "@ts-common/azure-js-dev-tools"; -process.exitCode = checkPackageJsonVersion({ startPath: __dirname }).check() as number; \ No newline at end of file +process.exitCode = checkPackageJsonVersion({ startPath: __dirname }).check() as number; diff --git a/lib/credentials/applicationTokenCertificateCredentials.ts b/lib/credentials/applicationTokenCertificateCredentials.ts index 3716f5e..ef069fa 100644 --- a/lib/credentials/applicationTokenCertificateCredentials.ts +++ b/lib/credentials/applicationTokenCertificateCredentials.ts @@ -101,19 +101,11 @@ export class ApplicationTokenCertificateCredentials extends ApplicationTokenCred domain: string, options: AzureTokenCredentialsOptions ): ApplicationTokenCertificateCredentials { - if ( - !certificateStringOrFilePath || - typeof certificateStringOrFilePath.valueOf() !== "string" - ) { - throw new Error( - "'certificateStringOrFilePath' must be a non empty string." - ); + if (!certificateStringOrFilePath || typeof certificateStringOrFilePath.valueOf() !== "string") { + throw new Error("'certificateStringOrFilePath' must be a non empty string."); } if (!certificateStringOrFilePath.startsWith("-----BEGIN")) { - certificateStringOrFilePath = readFileSync( - certificateStringOrFilePath, - "utf8" - ); + certificateStringOrFilePath = readFileSync(certificateStringOrFilePath, "utf8"); } const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9\+\/\n\r]+\=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/; const matchCert = certificateStringOrFilePath.match(certificatePattern); diff --git a/lib/credentials/applicationTokenCredentials.ts b/lib/credentials/applicationTokenCredentials.ts index 1609550..753872f 100644 --- a/lib/credentials/applicationTokenCredentials.ts +++ b/lib/credentials/applicationTokenCredentials.ts @@ -46,10 +46,7 @@ export class ApplicationTokenCredentials extends ApplicationTokenCredentialsBase try { return await this.getTokenFromCache(); } catch (error) { - if ( - error.message && - error.message.startsWith(AuthConstants.SDK_INTERNAL_ERROR) - ) { + if (error.message && error.message.startsWith(AuthConstants.SDK_INTERNAL_ERROR)) { throw error; } const resource = this.getActiveDirectoryResourceId(); diff --git a/lib/credentials/applicationTokenCredentialsBase.ts b/lib/credentials/applicationTokenCredentialsBase.ts index 7c6a5c4..205afa6 100644 --- a/lib/credentials/applicationTokenCredentialsBase.ts +++ b/lib/credentials/applicationTokenCredentialsBase.ts @@ -52,9 +52,9 @@ export abstract class ApplicationTokenCredentialsBase extends TokenCredentialsBa throw new Error( AuthConstants.SDK_INTERNAL_ERROR + - " : " + - "critical failure while removing expired token for service principal from token cache. " + - message + " : " + + "critical failure while removing expired token for service principal from token cache. " + + message ); } } @@ -71,7 +71,7 @@ export abstract class ApplicationTokenCredentialsBase extends TokenCredentialsBa query: object ): Promise<{ result: boolean; details?: Error }> { const self = this; - return new Promise<{ result: boolean; details?: Error }>(resolve => { + return new Promise<{ result: boolean; details?: Error }>((resolve) => { self.tokenCache.find(query, (error: Error, entries: any[]) => { if (error) { return resolve({ result: false, details: error }); diff --git a/lib/credentials/azureCliCredentials.ts b/lib/credentials/azureCliCredentials.ts index 2cab2b5..a375a58 100644 --- a/lib/credentials/azureCliCredentials.ts +++ b/lib/credentials/azureCliCredentials.ts @@ -109,7 +109,8 @@ export class AzureCliCredentials implements TokenClientCredentials { subscriptionInfo: LinkedSubscription, tokenInfo: CliAccessToken, // tslint:disable-next-line: no-inferrable-types - resource: string = "https://management.azure.com") { + resource: string = "https://management.azure.com" + ) { this.subscriptionInfo = subscriptionInfo; this.tokenInfo = tokenInfo; this.resource = resource; @@ -124,16 +125,14 @@ export class AzureCliCredentials implements TokenClientCredentials { if (this._hasTokenExpired() || this._hasSubscriptionChanged() || this._hasResourceChanged()) { try { // refresh the access token - this.tokenInfo = await AzureCliCredentials.getAccessToken( - { - subscriptionIdOrName: this.subscriptionInfo.id, - resource: this.resource - } - ); + this.tokenInfo = await AzureCliCredentials.getAccessToken({ + subscriptionIdOrName: this.subscriptionInfo.id, + resource: this.resource + }); } catch (err) { throw new Error( `An error occurred while refreshing the new access ` + - `token:${err.stderr ? err.stderr : err.message}` + `token:${err.stderr ? err.stderr : err.message}` ); } } @@ -162,9 +161,12 @@ export class AzureCliCredentials implements TokenClientCredentials { private _hasTokenExpired(): boolean { let result = true; const now = Math.floor(Date.now() / 1000); - if (this.tokenInfo.expiresOn && + if ( + this.tokenInfo.expiresOn && this.tokenInfo.expiresOn instanceof Date && - Math.floor(this.tokenInfo.expiresOn.getTime() / 1000) - now > this._tokenRenewalMarginInSeconds) { + Math.floor(this.tokenInfo.expiresOn.getTime() / 1000) - now > + this._tokenRenewalMarginInSeconds + ) { result = false; } return result; @@ -178,9 +180,14 @@ export class AzureCliCredentials implements TokenClientCredentials { try { const base64Url: string = this.tokenInfo.accessToken.split(".")[1]; const base64: string = decodeURIComponent( - Buffer.from(base64Url, "base64").toString("binary").split("").map((c) => { - return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); - }).join("")); + Buffer.from(base64Url, "base64") + .toString("binary") + .split("") + .map((c) => { + return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2); + }) + .join("") + ); return JSON.parse(base64); } catch (err) { @@ -192,22 +199,23 @@ export class AzureCliCredentials implements TokenClientCredentials { private _isAzureResourceManagerEndpoint(newResource: string, currentResource: string): boolean { if (newResource.endsWith("/")) newResource = newResource.slice(0, -1); if (currentResource.endsWith("/")) currentResource = currentResource.slice(0, -1); - return (newResource === "https://management.core.windows.net" && - currentResource === "https://management.azure.com") || + return ( + (newResource === "https://management.core.windows.net" && + currentResource === "https://management.azure.com") || (newResource === "https://management.azure.com" && - currentResource === "https://management.core.windows.net"); + currentResource === "https://management.core.windows.net") + ); } private _hasResourceChanged(): boolean { const parsedToken: ParsedToken = this._parseToken(); // normalize the resource string, since it is possible to // provide a resource without a trailing slash - const currentResource = parsedToken.aud && parsedToken.aud.endsWith("/") - ? parsedToken.aud.slice(0, -1) - : parsedToken.aud; - const newResource = this.resource.endsWith("/") - ? this.resource.slice(0, -1) - : this.resource; + const currentResource = + parsedToken.aud && parsedToken.aud.endsWith("/") + ? parsedToken.aud.slice(0, -1) + : parsedToken.aud; + const newResource = this.resource.endsWith("/") ? this.resource.slice(0, -1) : this.resource; const result = this._isAzureResourceManagerEndpoint(newResource, currentResource) ? false : currentResource !== newResource; @@ -232,8 +240,7 @@ export class AzureCliCredentials implements TokenClientCredentials { return result as CliAccessToken; } catch (err) { const message = - `An error occurred while getting credentials from ` + - `Azure CLI: ${err.stack}`; + `An error occurred while getting credentials from ` + `Azure CLI: ${err.stack}`; throw new Error(message); } } @@ -244,7 +251,10 @@ export class AzureCliCredentials implements TokenClientCredentials { * required. */ static async getSubscription(subscriptionIdOrName?: string): Promise { - if (subscriptionIdOrName && (typeof subscriptionIdOrName !== "string" || !subscriptionIdOrName.length)) { + if ( + subscriptionIdOrName && + (typeof subscriptionIdOrName !== "string" || !subscriptionIdOrName.length) + ) { throw new Error("'subscriptionIdOrName' must be a non-empty string."); } try { @@ -282,7 +292,9 @@ export class AzureCliCredentials implements TokenClientCredentials { * Returns a list of all the subscriptions from Azure CLI. * @param options Optional parameters that can be provided while listing all the subcriptions. */ - static async listAllSubscriptions(options: ListAllSubscriptionOptions = {}): Promise { + static async listAllSubscriptions( + options: ListAllSubscriptionOptions = {} + ): Promise { let subscriptionList: any[] = []; try { let cmd = "account list"; diff --git a/lib/credentials/deviceTokenCredentials.ts b/lib/credentials/deviceTokenCredentials.ts index d8c534d..195a520 100644 --- a/lib/credentials/deviceTokenCredentials.ts +++ b/lib/credentials/deviceTokenCredentials.ts @@ -7,7 +7,6 @@ import { AuthConstants, TokenAudience } from "../util/authConstants"; import { TokenResponse, TokenCache } from "adal-node"; export class DeviceTokenCredentials extends TokenCredentialsBase { - readonly username: string; /** @@ -34,8 +33,8 @@ export class DeviceTokenCredentials extends TokenCredentialsBase { username?: string, tokenAudience?: TokenAudience, environment?: Environment, - tokenCache?: TokenCache) { - + tokenCache?: TokenCache + ) { if (!username) { username = "user@example.com"; } @@ -57,4 +56,4 @@ export class DeviceTokenCredentials extends TokenCredentialsBase { // For device auth, this is just getTokenFromCache. return this.getTokenFromCache(this.username); } -} \ No newline at end of file +} diff --git a/lib/credentials/keyVaultFactory.ts b/lib/credentials/keyVaultFactory.ts index 856d3c3..9c218a9 100644 --- a/lib/credentials/keyVaultFactory.ts +++ b/lib/credentials/keyVaultFactory.ts @@ -33,51 +33,83 @@ function _convert(credentials: MSITokenCredentials): MSITokenCredentials { msiEndpoint: credentials.msiEndpoint }); } else if (credentials instanceof MSITokenCredentials) { - throw new Error("MSI-credentials not one of: MSIVmTokenCredentials, MSIAppServiceTokenCredentials"); + throw new Error( + "MSI-credentials not one of: MSIVmTokenCredentials, MSIAppServiceTokenCredentials" + ); } else { return credentials; } } function _createAuthenticatorMapper(credentials: MSITokenCredentials): Authenticator { - return (challenge: any) => new Promise((resolve, reject) => { - // Function to take token Response and format a authorization value - const _formAuthorizationValue = (err: Error, tokenResponse: TokenResponse | ErrorResponse) => { - if (err) { - return reject(err); - } + return (challenge: any) => + new Promise((resolve, reject) => { + // Function to take token Response and format a authorization value + const _formAuthorizationValue = ( + err: Error, + tokenResponse: TokenResponse | ErrorResponse + ) => { + if (err) { + return reject(err); + } - if (tokenResponse.error) { - return reject(tokenResponse.error); - } + if (tokenResponse.error) { + return reject(tokenResponse.error); + } - tokenResponse = tokenResponse as TokenResponse; - // Calculate the value to be set in the request's Authorization header and resume the call. - const authorizationValue = tokenResponse.tokenType + " " + tokenResponse.accessToken; - return resolve(authorizationValue); - }; + tokenResponse = tokenResponse as TokenResponse; + // Calculate the value to be set in the request's Authorization header and resume the call. + const authorizationValue = tokenResponse.tokenType + " " + tokenResponse.accessToken; + return resolve(authorizationValue); + }; - // Create a new authentication context. - if (credentials instanceof TokenCredentialsBase) { - const context = new AuthenticationContext(challenge.authorization, true, credentials.authContext && credentials.authContext.cache); - if (credentials instanceof ApplicationTokenCredentials) { - return context.acquireTokenWithClientCredentials( - challenge.resource, credentials.clientId, credentials.secret, _formAuthorizationValue); - } else if (credentials instanceof ApplicationTokenCertificateCredentials) { - return context.acquireTokenWithClientCertificate( - challenge.resource, credentials.clientId, credentials.certificate, credentials.thumbprint, _formAuthorizationValue); - } else if (credentials instanceof UserTokenCredentials) { - return context.acquireTokenWithUsernamePassword( - challenge.resource, credentials.username, credentials.password, credentials.clientId, _formAuthorizationValue); - } else if (credentials instanceof DeviceTokenCredentials) { - return context.acquireToken( - challenge.resource, credentials.username, credentials.clientId, _formAuthorizationValue); + // Create a new authentication context. + if (credentials instanceof TokenCredentialsBase) { + const context = new AuthenticationContext( + challenge.authorization, + true, + credentials.authContext && credentials.authContext.cache + ); + if (credentials instanceof ApplicationTokenCredentials) { + return context.acquireTokenWithClientCredentials( + challenge.resource, + credentials.clientId, + credentials.secret, + _formAuthorizationValue + ); + } else if (credentials instanceof ApplicationTokenCertificateCredentials) { + return context.acquireTokenWithClientCertificate( + challenge.resource, + credentials.clientId, + credentials.certificate, + credentials.thumbprint, + _formAuthorizationValue + ); + } else if (credentials instanceof UserTokenCredentials) { + return context.acquireTokenWithUsernamePassword( + challenge.resource, + credentials.username, + credentials.password, + credentials.clientId, + _formAuthorizationValue + ); + } else if (credentials instanceof DeviceTokenCredentials) { + return context.acquireToken( + challenge.resource, + credentials.username, + credentials.clientId, + _formAuthorizationValue + ); + } + } else if (credentials instanceof MSITokenCredentials) { + return credentials.getToken(); + } else { + return reject( + new Error( + "credentials must be one of: ApplicationTokenCredentials, UserTokenCredentials, " + + "DeviceTokenCredentials, MSITokenCredentials" + ) + ); } - } else if (credentials instanceof MSITokenCredentials) { - return credentials.getToken(); - } else { - return reject(new Error("credentials must be one of: ApplicationTokenCredentials, UserTokenCredentials, " + - "DeviceTokenCredentials, MSITokenCredentials")); - } - }); + }); } diff --git a/lib/credentials/msiAppServiceTokenCredentials.ts b/lib/credentials/msiAppServiceTokenCredentials.ts index 01e0d97..64ffb3c 100644 --- a/lib/credentials/msiAppServiceTokenCredentials.ts +++ b/lib/credentials/msiAppServiceTokenCredentials.ts @@ -70,16 +70,28 @@ export class MSIAppServiceTokenCredentials extends MSITokenCredentials { constructor(options?: MSIAppServiceOptions) { if (!options) options = {}; super(options); - options.msiEndpoint = options.msiEndpoint || process.env["IDENTITY_ENDPOINT"] || process.env["MSI_ENDPOINT"]; - options.msiSecret = options.msiSecret || process.env["IDENTITY_SECRET"] || process.env["MSI_SECRET"]; - if (!options.msiEndpoint || (options.msiEndpoint && typeof options.msiEndpoint.valueOf() !== "string")) { - throw new Error('Either provide "msiEndpoint" as a property of the "options" object ' + - 'or set the environment variable "IDENTITY_ENDPOINT" or "MSI_ENDPOINT" and it must be of type "string".'); + options.msiEndpoint = + options.msiEndpoint || process.env["IDENTITY_ENDPOINT"] || process.env["MSI_ENDPOINT"]; + options.msiSecret = + options.msiSecret || process.env["IDENTITY_SECRET"] || process.env["MSI_SECRET"]; + if ( + !options.msiEndpoint || + (options.msiEndpoint && typeof options.msiEndpoint.valueOf() !== "string") + ) { + throw new Error( + 'Either provide "msiEndpoint" as a property of the "options" object ' + + 'or set the environment variable "IDENTITY_ENDPOINT" or "MSI_ENDPOINT" and it must be of type "string".' + ); } - if (!options.msiSecret || (options.msiSecret && typeof options.msiSecret.valueOf() !== "string")) { - throw new Error('Either provide "msiSecret" as a property of the "options" object ' + - 'or set the environment variable "IDENTITY_SECRET" or "MSI_SECRET" and it must be of type "string".'); + if ( + !options.msiSecret || + (options.msiSecret && typeof options.msiSecret.valueOf() !== "string") + ) { + throw new Error( + 'Either provide "msiSecret" as a property of the "options" object ' + + 'or set the environment variable "IDENTITY_SECRET" or "MSI_SECRET" and it must be of type "string".' + ); } if (!options.msiApiVersion) { @@ -103,14 +115,20 @@ export class MSIAppServiceTokenCredentials extends MSITokenCredentials { const opRes = await this._httpClient.sendRequest(reqOptions); if (opRes.bodyAsText === undefined || opRes.bodyAsText!.indexOf("ExceptionMessage") !== -1) { - throw new Error(`MSI: Failed to retrieve a token from "${reqOptions.url}" with an error: ${opRes.bodyAsText}`); + throw new Error( + `MSI: Failed to retrieve a token from "${reqOptions.url}" with an error: ${opRes.bodyAsText}` + ); } const result = this.parseTokenResponse(opRes.bodyAsText!) as MSITokenResponse; if (!result.tokenType) { - throw new Error(`Invalid token response, did not find tokenType. Response body is: ${opRes.bodyAsText}`); + throw new Error( + `Invalid token response, did not find tokenType. Response body is: ${opRes.bodyAsText}` + ); } else if (!result.accessToken) { - throw new Error(`Invalid token response, did not find accessToken. Response body is: ${opRes.bodyAsText}`); + throw new Error( + `Invalid token response, did not find accessToken. Response body is: ${opRes.bodyAsText}` + ); } return result; @@ -121,14 +139,14 @@ export class MSIAppServiceTokenCredentials extends MSITokenCredentials { const reqOptions: RequestPrepareOptions = { url: endpoint, headers: { - secret: this.msiSecret, + secret: this.msiSecret }, queryParameters: { - "resource": this.resource, + resource: this.resource, "api-version": this.msiApiVersion, - "clientid": this.clientId, + clientid: this.clientId }, - method: "GET", + method: "GET" }; const webResource = new WebResource(); diff --git a/lib/credentials/msiTokenCredentials.ts b/lib/credentials/msiTokenCredentials.ts index 3337c51..b2d7d0d 100644 --- a/lib/credentials/msiTokenCredentials.ts +++ b/lib/credentials/msiTokenCredentials.ts @@ -144,7 +144,10 @@ export abstract class MSITokenCredentials implements TokenClientCredentials { */ public async signRequest(webResource: WebResource): Promise { const tokenResponse = await this.getToken(); - webResource.headers.set(Constants.HeaderConstants.AUTHORIZATION, `${tokenResponse.tokenType} ${tokenResponse.accessToken}`); + webResource.headers.set( + Constants.HeaderConstants.AUTHORIZATION, + `${tokenResponse.tokenType} ${tokenResponse.accessToken}` + ); return webResource; } } diff --git a/lib/credentials/msiVmTokenCredentials.ts b/lib/credentials/msiVmTokenCredentials.ts index 82f08f4..ecc9bca 100644 --- a/lib/credentials/msiVmTokenCredentials.ts +++ b/lib/credentials/msiVmTokenCredentials.ts @@ -97,12 +97,15 @@ export class MSIVmTokenCredentials extends MSITokenCredentials { const opRes = await this._httpClient.sendRequest(reqOptions); const result = this.parseTokenResponse(opRes.bodyAsText!) as MSITokenResponse; if (!result.tokenType) { - throw new Error(`Invalid token response, did not find tokenType. Response body is: ${opRes.bodyAsText}`); + throw new Error( + `Invalid token response, did not find tokenType. Response body is: ${opRes.bodyAsText}` + ); } else if (!result.accessToken) { - throw new Error(`Invalid token response, did not find accessToken. Response body is: ${opRes.bodyAsText}`); + throw new Error( + `Invalid token response, did not find accessToken. Response body is: ${opRes.bodyAsText}` + ); } - return result; } @@ -111,15 +114,15 @@ export class MSIVmTokenCredentials extends MSITokenCredentials { url: this.msiEndpoint, headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", - "Metadata": "true" + Metadata: "true" }, method: this.httpMethod, queryParameters: { "api-version": this.apiVersion, - "resource": this.resource, - "object_id": this.objectId, - "client_id": this.clientId, - "mi_res_id": this.identityId + resource: this.resource, + object_id: this.objectId, + client_id: this.clientId, + mi_res_id: this.identityId } }; diff --git a/lib/credentials/tokenCredentialsBase.ts b/lib/credentials/tokenCredentialsBase.ts index d518775..9fd6dd3 100644 --- a/lib/credentials/tokenCredentialsBase.ts +++ b/lib/credentials/tokenCredentialsBase.ts @@ -10,7 +10,7 @@ import { AuthenticationContext, MemoryCache, ErrorResponse, - TokenCache, + TokenCache } from "adal-node"; export abstract class TokenCredentialsBase implements TokenClientCredentials { @@ -31,18 +31,14 @@ export abstract class TokenCredentialsBase implements TokenClientCredentials { throw new Error("domain must be a non empty string."); } - if ( - this.tokenAudience === "graph" && - this.domain.toLowerCase() === "common" - ) { + if (this.tokenAudience === "graph" && this.domain.toLowerCase() === "common") { throw new Error( `${'If the tokenAudience is specified as "graph" then "domain" cannot be defaulted to "common" tenant.\ It must be the actual tenant (preferably a string in a guid format).'}` ); } - const authorityUrl = - this.environment.activeDirectoryEndpointUrl + this.domain; + const authorityUrl = this.environment.activeDirectoryEndpointUrl + this.domain; this.authContext = new AuthenticationContext( authorityUrl, this.environment.validateAuthority, @@ -52,8 +48,7 @@ export abstract class TokenCredentialsBase implements TokenClientCredentials { public setDomain(domain: string): void { this.domain = domain; - const authorityUrl = - this.environment.activeDirectoryEndpointUrl + this.domain; + const authorityUrl = this.environment.activeDirectoryEndpointUrl + this.domain; this.authContext = new AuthenticationContext( authorityUrl, this.environment.validateAuthority, diff --git a/lib/credentials/userTokenCredentials.ts b/lib/credentials/userTokenCredentials.ts index 6c47420..233d4b7 100644 --- a/lib/credentials/userTokenCredentials.ts +++ b/lib/credentials/userTokenCredentials.ts @@ -7,7 +7,6 @@ import { TokenAudience } from "../util/authConstants"; import { TokenResponse, ErrorResponse, TokenCache } from "adal-node"; export class UserTokenCredentials extends TokenCredentialsBase { - readonly username: string; readonly password: string; @@ -33,8 +32,8 @@ export class UserTokenCredentials extends TokenCredentialsBase { password: string, tokenAudience?: TokenAudience, environment?: Environment, - tokenCache?: TokenCache) { - + tokenCache?: TokenCache + ) { if (!clientId || typeof clientId.valueOf() !== "string") { throw new Error("clientId must be a non empty string."); } @@ -60,7 +59,7 @@ export class UserTokenCredentials extends TokenCredentialsBase { private crossCheckUserNameWithToken(username: string, userIdFromToken: string): boolean { // to maintain the casing consistency between "azureprofile.json" and token cache. (RD 1996587) // use the "userId" here, which should be the same with "username" except the casing. - return (username.toLowerCase() === userIdFromToken.toLowerCase()); + return username.toLowerCase() === userIdFromToken.toLowerCase(); } /** @@ -76,7 +75,11 @@ export class UserTokenCredentials extends TokenCredentialsBase { const resource = this.getActiveDirectoryResourceId(); return new Promise((resolve, reject) => { - self.authContext.acquireTokenWithUsernamePassword(resource, self.username, self.password, self.clientId, + self.authContext.acquireTokenWithUsernamePassword( + resource, + self.username, + self.password, + self.clientId, (error: Error, tokenResponse: TokenResponse | ErrorResponse) => { if (error) { return reject(error); @@ -88,12 +91,15 @@ export class UserTokenCredentials extends TokenCredentialsBase { tokenResponse = tokenResponse as TokenResponse; if (self.crossCheckUserNameWithToken(self.username, tokenResponse.userId!)) { - return resolve((tokenResponse as TokenResponse)); + return resolve(tokenResponse as TokenResponse); } else { - return reject(`The userId "${tokenResponse.userId}" in access token doesn't match the username "${self.username}" provided during authentication.`); + return reject( + `The userId "${tokenResponse.userId}" in access token doesn't match the username "${self.username}" provided during authentication.` + ); } - }); + } + ); }); } } -} \ No newline at end of file +} diff --git a/lib/login.ts b/lib/login.ts index 46c8f12..f601ed5 100644 --- a/lib/login.ts +++ b/lib/login.ts @@ -12,9 +12,16 @@ import { ApplicationTokenCertificateCredentials } from "./credentials/applicatio import { DeviceTokenCredentials } from "./credentials/deviceTokenCredentials"; import { UserTokenCredentials } from "./credentials/userTokenCredentials"; import { AuthConstants, TokenAudience } from "./util/authConstants"; -import { buildTenantList, getSubscriptionsFromTenants, LinkedSubscription } from "./subscriptionManagement/subscriptionUtils"; +import { + buildTenantList, + getSubscriptionsFromTenants, + LinkedSubscription +} from "./subscriptionManagement/subscriptionUtils"; import { MSIVmTokenCredentials, MSIVmOptions } from "./credentials/msiVmTokenCredentials"; -import { MSIAppServiceTokenCredentials, MSIAppServiceOptions } from "./credentials/msiAppServiceTokenCredentials"; +import { + MSIAppServiceTokenCredentials, + MSIAppServiceOptions +} from "./credentials/msiAppServiceTokenCredentials"; import { MSITokenResponse } from "./credentials/msiTokenCredentials"; /** @@ -36,17 +43,16 @@ const managementPlaneTokenAudiences = [ 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); - } + 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"]) { @@ -160,7 +166,11 @@ export type Callback = (error?: Error, result?: TResult) => void; * * @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 { +export async function withUsernamePasswordWithAuthResponse( + username: string, + password: string, + options?: LoginWithUsernamePasswordOptions +): Promise { if (!options) { options = {}; } @@ -174,7 +184,15 @@ export async function withUsernamePasswordWithAuthResponse(username: string, pas options.environment = Environment.AzureCloud; } - const creds = new UserTokenCredentials(options.clientId, options.domain, username, password, options.tokenAudience, options.environment, options.tokenCache); + 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. @@ -183,7 +201,11 @@ export async function withUsernamePasswordWithAuthResponse(username: string, pas tenantList = [tokenResponse.tenantId]; } - const subscriptionList: LinkedSubscription[] = await _getSubscriptions(creds, tenantList, options.tokenAudience); + const subscriptionList: LinkedSubscription[] = await _getSubscriptions( + creds, + tenantList, + options.tokenAudience + ); return { credentials: creds, subscriptions: subscriptionList }; } @@ -206,7 +228,12 @@ export async function withUsernamePasswordWithAuthResponse(username: string, pas * * @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 { +export async function withServicePrincipalSecretWithAuthResponse( + clientId: string, + secret: string, + domain: string, + options?: AzureTokenCredentialsOptions +): Promise { if (!options) { options = {}; } @@ -214,7 +241,14 @@ export async function withServicePrincipalSecretWithAuthResponse(clientId: strin options.environment = Environment.AzureCloud; } - const creds = new ApplicationTokenCredentials(clientId, domain, secret, options.tokenAudience, options.environment, options.tokenCache); + const creds = new ApplicationTokenCredentials( + clientId, + domain, + secret, + options.tokenAudience, + options.environment, + options.tokenCache + ); await creds.getToken(); const subscriptionList = await _getSubscriptions(creds, [domain], options.tokenAudience); @@ -242,7 +276,12 @@ export async function withServicePrincipalSecretWithAuthResponse(clientId: strin * * @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 { +export async function withServicePrincipalCertificateWithAuthResponse( + clientId: string, + certificateStringOrFilePath: string, + domain: string, + options?: AzureTokenCredentialsOptions +): Promise { if (!options) { options = {}; } @@ -250,7 +289,12 @@ export async function withServicePrincipalCertificateWithAuthResponse(clientId: options.environment = Environment.AzureCloud; } - const creds = ApplicationTokenCertificateCredentials.create(clientId, certificateStringOrFilePath, domain, options); + const creds = ApplicationTokenCertificateCredentials.create( + clientId, + certificateStringOrFilePath, + domain, + options + ); await creds.getToken(); const subscriptionList = await _getSubscriptions(creds, [domain], options.tokenAudience); @@ -269,7 +313,9 @@ function validateAuthFileContent(credsObj: any, filePath: string) { 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}.`); + 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}.`); @@ -302,7 +348,7 @@ function foundManagementEndpointUrl(authFileUrl: string, envUrl: string): boolea authFileUrl = authFileUrl.endsWith("/") ? authFileUrl.slice(0, -1) : authFileUrl; envUrl = envUrl.endsWith("/") ? envUrl.slice(0, -1) : envUrl; - return (authFileUrl.toLowerCase() === envUrl.toLowerCase()); + return authFileUrl.toLowerCase() === envUrl.toLowerCase(); } /** @@ -330,15 +376,19 @@ function foundManagementEndpointUrl(authFileUrl: string, envUrl: string): boolea * * @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 { +export async function withAuthFileWithAuthResponse( + options?: LoginWithAuthFileOptions +): Promise { if (!options) options = { filePath: "" }; const filePath = options.filePath || process.env[AuthConstants.AZURE_AUTH_LOCATION]; - const subscriptionEnvVariableName = options.subscriptionEnvVariableName || "AZURE_SUBSCRIPTION_ID"; + 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 = {}; + let content: string, + credsObj: any = {}; const optionsForSp: any = {}; content = readFileSync(filePath, { encoding: "utf8" }); @@ -358,9 +408,14 @@ export async function withAuthFileWithAuthResponse(options?: LoginWithAuthFileOp for (let i = 0; i < envNames.length; i++) { const env = envNames[i]; const environmentObj = (Environment as any)[env]; - if (environmentObj && + if ( + environmentObj && environmentObj.managementEndpointUrl && - foundManagementEndpointUrl(credsObj.managementEndpointUrl, environmentObj.managementEndpointUrl)) { + foundManagementEndpointUrl( + credsObj.managementEndpointUrl, + environmentObj.managementEndpointUrl + ) + ) { envFound.name = environmentObj.name; break; } @@ -376,7 +431,9 @@ export async function withAuthFileWithAuthResponse(options?: LoginWithAuthFileOp 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)$/ig) === null) { + if ( + key.match(/^(clientId|clientSecret|clientCertificate|subscriptionId|tenantId)$/gi) === null + ) { if (key === "activeDirectoryEndpointUrl" && !key.endsWith("/")) { envParams[key] = credsObj[key] + "/"; } else { @@ -393,13 +450,22 @@ export async function withAuthFileWithAuthResponse(options?: LoginWithAuthFileOp optionsForSp.environment = Environment.add(envParams); } if (credsObj.clientSecret) { - return withServicePrincipalSecretWithAuthResponse(credsObj.clientId, credsObj.clientSecret, credsObj.tenantId, optionsForSp); + return withServicePrincipalSecretWithAuthResponse( + credsObj.clientId, + credsObj.clientSecret, + credsObj.tenantId, + optionsForSp + ); } - return withServicePrincipalCertificateWithAuthResponse(credsObj.clientId, credsObj.clientCertificate, 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. * @@ -429,7 +495,9 @@ export async function withAuthFileWithAuthResponse(options?: LoginWithAuthFileOp * * @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 { +export async function withInteractiveWithAuthResponse( + options?: InteractiveLoginOptions +): Promise { if (!options) { options = {}; } @@ -465,32 +533,42 @@ export async function withInteractiveWithAuthResponse(options?: InteractiveLogin 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); + 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); + 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; } - return; - } + if (interactiveOptions.userCodeResponseLogger) { + interactiveOptions.userCodeResponseLogger(userCodeRes.message); + } else { + console.log(userCodeRes.message); + } - if (interactiveOptions.userCodeResponseLogger) { - interactiveOptions.userCodeResponseLogger(userCodeRes.message); - } else { - console.log(userCodeRes.message); + return resolve(userCodeRes); } - - return resolve(userCodeRes); - }); + ); } const getUserCode = new Promise((resolve, reject) => { @@ -499,24 +577,35 @@ export async function withInteractiveWithAuthResponse(options?: InteractiveLogin const userCodeResponse = await getUserCode; const creds = await new Promise((resolve, reject) => { - return authContext.acquireTokenWithDeviceCode(interactiveOptions.tokenAudience, interactiveOptions.clientId, userCodeResponse, (error, tokenResponse) => { - if (error) { - return reject(error); - } + 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; + 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); + 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); } - return resolve(creds); - }); + ); }); const tenants = await buildTenantList(creds); @@ -560,9 +649,27 @@ export async function withInteractiveWithAuthResponse(options?: InteractiveLogin */ export function withAuthFile(): Promise; export function withAuthFile(options: LoginWithAuthFileOptions): Promise; -export function withAuthFile(options: LoginWithAuthFileOptions, callback: { (err: Error, credentials: ApplicationTokenCredentials, subscriptions: Array): void }): void; +export function withAuthFile( + options: LoginWithAuthFileOptions, + callback: { + ( + err: Error, + credentials: ApplicationTokenCredentials, + subscriptions: Array + ): void; + } +): void; export function withAuthFile(callback: any): void; -export function withAuthFile(options?: LoginWithAuthFileOptions, callback?: { (err: Error, credentials: ApplicationTokenCredentials, subscriptions: Array): void }): any { +export function withAuthFile( + options?: LoginWithAuthFileOptions, + callback?: { + ( + err: Error, + credentials: ApplicationTokenCredentials, + subscriptions: Array + ): void; + } +): any { if (!callback && typeof options === "function") { callback = options; options = undefined; @@ -573,12 +680,14 @@ export function withAuthFile(options?: LoginWithAuthFileOptions, callback?: { (e return authRes.credentials; }); } else { - msRest.promiseToCallback(withAuthFileWithAuthResponse(options))((err: Error, authRes: AuthResponse) => { - if (err) { - return cb(err); + msRest.promiseToCallback(withAuthFileWithAuthResponse(options))( + (err: Error, authRes: AuthResponse) => { + if (err) { + return cb(err); + } + return cb(undefined, authRes.credentials, authRes.subscriptions); } - return cb(undefined, authRes.credentials, authRes.subscriptions); - }); + ); } } @@ -613,9 +722,27 @@ export function withAuthFile(options?: LoginWithAuthFileOptions, callback?: { (e */ export function interactive(): Promise; export function interactive(options: InteractiveLoginOptions): Promise; -export function interactive(options: InteractiveLoginOptions, callback: { (err: Error, credentials: DeviceTokenCredentials, subscriptions: Array): void }): void; +export function interactive( + options: InteractiveLoginOptions, + callback: { + ( + err: Error, + credentials: DeviceTokenCredentials, + subscriptions: Array + ): void; + } +): void; export function interactive(callback: any): void; -export function interactive(options?: InteractiveLoginOptions, callback?: { (err: Error, credentials: DeviceTokenCredentials, subscriptions: Array): void }): any { +export function interactive( + options?: InteractiveLoginOptions, + callback?: { + ( + err: Error, + credentials: DeviceTokenCredentials, + subscriptions: Array + ): void; + } +): any { if (!callback && typeof options === "function") { callback = options; options = undefined; @@ -626,12 +753,14 @@ export function interactive(options?: InteractiveLoginOptions, callback?: { (err return authRes.credentials; }); } else { - msRest.promiseToCallback(withInteractiveWithAuthResponse(options))((err: Error, authRes: AuthResponse) => { - if (err) { - return cb(err); + msRest.promiseToCallback(withInteractiveWithAuthResponse(options))( + (err: Error, authRes: AuthResponse) => { + if (err) { + return cb(err); + } + return cb(undefined, authRes.credentials, authRes.subscriptions); } - return cb(undefined, authRes.credentials, authRes.subscriptions); - }); + ); } } @@ -662,22 +791,64 @@ export function interactive(options?: InteractiveLoginOptions, callback?: { (err * @resolve {ApplicationTokenCredentials} The ApplicationTokenCredentials object. * @reject {Error} - The error object. */ -export function withServicePrincipalSecret(clientId: string, secret: string, domain: string): Promise; -export function withServicePrincipalSecret(clientId: string, secret: string, domain: string, options: AzureTokenCredentialsOptions): Promise; -export function withServicePrincipalSecret(clientId: string, secret: string, domain: string, options: AzureTokenCredentialsOptions, callback: { (err: Error, credentials: ApplicationTokenCredentials, subscriptions: Array): 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): void }): any { +export function withServicePrincipalSecret( + clientId: string, + secret: string, + domain: string +): Promise; +export function withServicePrincipalSecret( + clientId: string, + secret: string, + domain: string, + options: AzureTokenCredentialsOptions +): Promise; +export function withServicePrincipalSecret( + clientId: string, + secret: string, + domain: string, + options: AzureTokenCredentialsOptions, + callback: { + ( + err: Error, + credentials: ApplicationTokenCredentials, + subscriptions: Array + ): 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 + ): 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; - }); + return withServicePrincipalSecretWithAuthResponse(clientId, secret, domain, options).then( + (authRes) => { + return authRes.credentials; + } + ); } else { - msRest.promiseToCallback(withServicePrincipalSecretWithAuthResponse(clientId, secret, domain, options))((err: Error, authRes: AuthResponse) => { + msRest.promiseToCallback( + withServicePrincipalSecretWithAuthResponse(clientId, secret, domain, options) + )((err: Error, authRes: AuthResponse) => { if (err) { return cb(err); } @@ -715,22 +886,72 @@ export function withServicePrincipalSecret(clientId: string, secret: string, dom * @resolve {ApplicationTokenCertificateCredentials} The ApplicationTokenCertificateCredentials object. * @reject {Error} - The error object. */ -export function withServicePrincipalCertificate(clientId: string, certificateStringOrFilePath: string, domain: string): Promise; -export function withServicePrincipalCertificate(clientId: string, certificateStringOrFilePath: string, domain: string, options: AzureTokenCredentialsOptions): Promise; -export function withServicePrincipalCertificate(clientId: string, certificateStringOrFilePath: string, domain: string, options: AzureTokenCredentialsOptions, callback: { (err: Error, credentials: ApplicationTokenCertificateCredentials, subscriptions: Array): 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): void }): any { +export function withServicePrincipalCertificate( + clientId: string, + certificateStringOrFilePath: string, + domain: string +): Promise; +export function withServicePrincipalCertificate( + clientId: string, + certificateStringOrFilePath: string, + domain: string, + options: AzureTokenCredentialsOptions +): Promise; +export function withServicePrincipalCertificate( + clientId: string, + certificateStringOrFilePath: string, + domain: string, + options: AzureTokenCredentialsOptions, + callback: { + ( + err: Error, + credentials: ApplicationTokenCertificateCredentials, + subscriptions: Array + ): 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 + ): 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 withServicePrincipalCertificateWithAuthResponse( + clientId, + certificateStringOrFilePath, + domain, + options + ).then((authRes) => { return authRes.credentials; }); } else { - msRest.promiseToCallback(withServicePrincipalCertificateWithAuthResponse(clientId, certificateStringOrFilePath, domain, options))((err: Error, authRes: AuthResponse) => { + msRest.promiseToCallback( + withServicePrincipalCertificateWithAuthResponse( + clientId, + certificateStringOrFilePath, + domain, + options + ) + )((err: Error, authRes: AuthResponse) => { if (err) { return cb(err); } @@ -769,11 +990,32 @@ export function withServicePrincipalCertificate(clientId: string, certificateStr * @resolve {UserTokenCredentials} The UserTokenCredentials object. * @reject {Error} - The error object. */ -export function withUsernamePassword(username: string, password: string): Promise; -export function withUsernamePassword(username: string, password: string, options: LoginWithUsernamePasswordOptions): Promise; +export function withUsernamePassword( + username: string, + password: string +): Promise; +export function withUsernamePassword( + username: string, + password: string, + options: LoginWithUsernamePasswordOptions +): Promise; 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): void }): void; -export function withUsernamePassword(username: string, password: string, options?: LoginWithUsernamePasswordOptions, callback?: { (err: Error, credentials: UserTokenCredentials, subscriptions: Array): void }): any { +export function withUsernamePassword( + username: string, + password: string, + options: LoginWithUsernamePasswordOptions, + callback: { + (err: Error, credentials: UserTokenCredentials, subscriptions: Array): void; + } +): void; +export function withUsernamePassword( + username: string, + password: string, + options?: LoginWithUsernamePasswordOptions, + callback?: { + (err: Error, credentials: UserTokenCredentials, subscriptions: Array): void; + } +): any { if (!callback && typeof options === "function") { callback = options; options = undefined; @@ -784,12 +1026,14 @@ export function withUsernamePassword(username: string, password: string, options return authRes.credentials; }); } else { - msRest.promiseToCallback(withUsernamePasswordWithAuthResponse(username, password, options))((err: Error, authRes: AuthResponse) => { - if (err) { - return cb(err); + msRest.promiseToCallback(withUsernamePasswordWithAuthResponse(username, password, options))( + (err: Error, authRes: AuthResponse) => { + if (err) { + return cb(err); + } + return cb(undefined, authRes.credentials, authRes.subscriptions); } - return cb(undefined, authRes.credentials, authRes.subscriptions); - }); + ); } } @@ -799,9 +1043,14 @@ export function withUsernamePassword(username: string, password: string, options function _getSubscriptions( creds: TokenCredentialsBase, tenants: string[], - tokenAudience?: string): Promise { - if (tokenAudience && - !managementPlaneTokenAudiences.some((item) => { return item === tokenAudience!.toLowerCase(); })) { + tokenAudience?: string +): Promise { + if ( + tokenAudience && + !managementPlaneTokenAudiences.some((item) => { + return item === tokenAudience!.toLowerCase(); + }) + ) { return Promise.resolve([]); } return getSubscriptionsFromTenants(creds, tenants); @@ -865,9 +1114,15 @@ async function _withMSI(options?: MSIVmOptions): Promise */ export function loginWithVmMSI(): Promise; export function loginWithVmMSI(options: MSIVmOptions): Promise; -export function loginWithVmMSI(options: MSIVmOptions, callback: Callback): void; +export function loginWithVmMSI( + options: MSIVmOptions, + callback: Callback +): void; export function loginWithVmMSI(callback: Callback): void; -export function loginWithVmMSI(options?: MSIVmOptions | Callback, callback?: Callback): void | Promise { +export function loginWithVmMSI( + options?: MSIVmOptions | Callback, + callback?: Callback +): void | Promise { if (!callback && typeof options === "function") { callback = options; options = {}; @@ -876,19 +1131,23 @@ export function loginWithVmMSI(options?: MSIVmOptions | Callback { - if (err) { - return cb(err); + msRest.promiseToCallback(_withMSI(options as MSIVmOptions))( + (err: Error, tokenRes: MSITokenResponse) => { + if (err) { + return cb(err); + } + return cb(undefined, tokenRes); } - return cb(undefined, tokenRes); - }); + ); } } /** * Private method */ -async function _withAppServiceMSI(options: MSIAppServiceOptions): Promise { +async function _withAppServiceMSI( + options: MSIAppServiceOptions +): Promise { if (!options) { options = {}; } @@ -923,10 +1182,18 @@ async function _withAppServiceMSI(options: MSIAppServiceOptions): Promise; -export function loginWithAppServiceMSI(options: MSIAppServiceOptions): Promise; -export function loginWithAppServiceMSI(options: MSIAppServiceOptions, callback: Callback): void; +export function loginWithAppServiceMSI( + options: MSIAppServiceOptions +): Promise; +export function loginWithAppServiceMSI( + options: MSIAppServiceOptions, + callback: Callback +): void; export function loginWithAppServiceMSI(callback: Callback): void; -export function loginWithAppServiceMSI(options?: MSIAppServiceOptions | Callback, callback?: Callback): void | Promise { +export function loginWithAppServiceMSI( + options?: MSIAppServiceOptions | Callback, + callback?: Callback +): void | Promise { if (!callback && typeof options === "function") { callback = options; options = {}; @@ -935,12 +1202,14 @@ export function loginWithAppServiceMSI(options?: MSIAppServiceOptions | Callback if (!callback) { return _withAppServiceMSI(options as MSIAppServiceOptions); } else { - msRest.promiseToCallback(_withAppServiceMSI(options as MSIAppServiceOptions))((err: Error, tokenRes: MSITokenResponse) => { - if (err) { - return cb(err); + msRest.promiseToCallback(_withAppServiceMSI(options as MSIAppServiceOptions))( + (err: Error, tokenRes: MSITokenResponse) => { + if (err) { + return cb(err); + } + return cb(undefined, tokenRes); } - return cb(undefined, tokenRes); - }); + ); } } @@ -959,7 +1228,8 @@ export async function execAz(cmd: string): Promise { try { return resolve(JSON.parse(stdout)); } catch (err) { - const msg = `An error occurred while parsing the output "${stdout}", of ` + + const msg = + `An error occurred while parsing the output "${stdout}", of ` + `the cmd "${cmd}": ${err.stack}.`; return reject(new Error(msg)); } @@ -967,5 +1237,4 @@ export async function execAz(cmd: string): Promise { return resolve(); }); }); - } diff --git a/lib/msRestNodeAuth.ts b/lib/msRestNodeAuth.ts index 4b61e7f..055cfb3 100644 --- a/lib/msRestNodeAuth.ts +++ b/lib/msRestNodeAuth.ts @@ -5,21 +5,36 @@ export { ApplicationTokenCredentials } from "./credentials/applicationTokenCrede export { ApplicationTokenCertificateCredentials } from "./credentials/applicationTokenCertificateCredentials"; export { DeviceTokenCredentials } from "./credentials/deviceTokenCredentials"; export { createAuthenticator } from "./credentials/keyVaultFactory"; -export { MSIAppServiceOptions, MSIAppServiceTokenCredentials } from "./credentials/msiAppServiceTokenCredentials"; -export { MSIOptions, MSITokenCredentials, MSITokenResponse } from "./credentials/msiTokenCredentials"; +export { + MSIAppServiceOptions, + MSIAppServiceTokenCredentials +} from "./credentials/msiAppServiceTokenCredentials"; +export { + MSIOptions, + MSITokenCredentials, + MSITokenResponse +} from "./credentials/msiTokenCredentials"; export { MSIVmOptions, MSIVmTokenCredentials } from "./credentials/msiVmTokenCredentials"; export { TokenCredentialsBase } from "./credentials/tokenCredentialsBase"; export { UserTokenCredentials } from "./credentials/userTokenCredentials"; export { AuthConstants, TokenAudience } from "./util/authConstants"; -export { LinkedSubscription, LinkedUser, UserType, buildTenantList } from "./subscriptionManagement/subscriptionUtils"; +export { + LinkedSubscription, + LinkedUser, + UserType, + buildTenantList +} from "./subscriptionManagement/subscriptionUtils"; export { AzureCliCredentials, CliAccessToken, ListAllSubscriptionOptions } from "./credentials/azureCliCredentials"; export { - AuthResponse, LoginWithAuthFileOptions, InteractiveLoginOptions, - AzureTokenCredentialsOptions, LoginWithUsernamePasswordOptions, + AuthResponse, + LoginWithAuthFileOptions, + InteractiveLoginOptions, + AzureTokenCredentialsOptions, + LoginWithUsernamePasswordOptions, interactive as interactiveLogin, withInteractiveWithAuthResponse as interactiveLoginWithAuthResponse, withUsernamePassword as loginWithUsernamePassword, diff --git a/lib/subscriptionManagement/subscriptionUtils.ts b/lib/subscriptionManagement/subscriptionUtils.ts index 891f0a4..3ef329c 100644 --- a/lib/subscriptionManagement/subscriptionUtils.ts +++ b/lib/subscriptionManagement/subscriptionUtils.ts @@ -76,7 +76,10 @@ export interface LinkedSubscription { * @param apiVersion - default value 2016-06-01 * @returns A promise that resolves to an array of tenantIds and rejects with an error. */ -export async function buildTenantList(credentials: TokenCredentialsBase, apiVersion = "2016-06-01"): Promise { +export async function buildTenantList( + credentials: TokenCredentialsBase, + apiVersion = "2016-06-01" +): Promise { if (credentials.domain && credentials.domain !== AuthConstants.AAD_COMMON_TENANT) { return [credentials.domain]; } @@ -86,7 +89,7 @@ export async function buildTenantList(credentials: TokenCredentialsBase, apiVers const reqUrl = `${baseUrl}${baseUrl.endsWith("/") ? "" : "/"}tenants?api-version=${apiVersion}`; const req: msRest.RequestPrepareOptions = { url: reqUrl, - method: "GET", + method: "GET" }; const res = await client.sendRequest(req); const result: string[] = []; @@ -101,7 +104,11 @@ export async function buildTenantList(credentials: TokenCredentialsBase, apiVers return result; } -export async function getSubscriptionsFromTenants(credentials: TokenCredentialsBase, tenantList: string[], apiVersion = "2016-06-01"): Promise { +export async function getSubscriptionsFromTenants( + credentials: TokenCredentialsBase, + tenantList: string[], + apiVersion = "2016-06-01" +): Promise { let subscriptions: LinkedSubscription[] = []; let userType = "user"; let username: string; @@ -116,25 +123,29 @@ export async function getSubscriptionsFromTenants(credentials: TokenCredentialsB credentials.domain = tenant; const client = new msRest.ServiceClient(credentials); const baseUrl = credentials.environment.resourceManagerEndpointUrl; - const reqUrl = `${baseUrl}${baseUrl.endsWith("/") ? "" : "/"}subscriptions?api-version=${apiVersion}`; + const reqUrl = `${baseUrl}${ + baseUrl.endsWith("/") ? "" : "/" + }subscriptions?api-version=${apiVersion}`; const req: msRest.RequestPrepareOptions = { url: reqUrl, - method: "GET", + method: "GET" }; const res = await client.sendRequest(req); const subscriptionList: any[] = (res.parsedBody).value; - subscriptions = subscriptions.concat(subscriptionList.map((s: any) => { - s.tenantId = tenant; - s.user = { name: username, type: userType }; - s.environmentName = credentials.environment.name; - s.name = s.displayName; - s.id = s.subscriptionId; - delete s.displayName; - delete s.subscriptionId; - delete s.subscriptionPolicies; - return s; - })); + subscriptions = subscriptions.concat( + subscriptionList.map((s: any) => { + s.tenantId = tenant; + s.user = { name: username, type: userType }; + s.environmentName = credentials.environment.name; + s.name = s.displayName; + s.id = s.subscriptionId; + delete s.displayName; + delete s.subscriptionId; + delete s.subscriptionPolicies; + return s; + }) + ); } // Reset the original domain. credentials.domain = originalDomain; diff --git a/lib/util/authConstants.ts b/lib/util/authConstants.ts index d7ea685..8919ebe 100644 --- a/lib/util/authConstants.ts +++ b/lib/util/authConstants.ts @@ -2,12 +2,12 @@ // Licensed under the MIT License. See License.txt in the project root for license information. export const AuthConstants = { - "AAD_COMMON_TENANT": "common", - "DEFAULT_ADAL_CLIENT_ID": "04b07795-8ddb-461a-bbee-02f9e1bf7b46", - "SDK_INTERNAL_ERROR": "SDK_INTERNAL_ERROR", - "DEFAULT_LANGUAGE": "en-us", - "AZURE_AUTH_LOCATION": "AZURE_AUTH_LOCATION", - "RESOURCE_MANAGER_ENDPOINT": "https://management.azure.com/" + AAD_COMMON_TENANT: "common", + DEFAULT_ADAL_CLIENT_ID: "04b07795-8ddb-461a-bbee-02f9e1bf7b46", + SDK_INTERNAL_ERROR: "SDK_INTERNAL_ERROR", + DEFAULT_LANGUAGE: "en-us", + AZURE_AUTH_LOCATION: "AZURE_AUTH_LOCATION", + RESOURCE_MANAGER_ENDPOINT: "https://management.azure.com/" }; export type TokenAudience = "graph" | "batch" | string | undefined; diff --git a/package.json b/package.json index 48cd840..fe08a76 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "nock": "^10.0.6", "npm-run-all": "^4.1.5", "nyc": "^14.1.1", + "prettier": "2.2.1", "rollup": "^1.18.0", "rollup-plugin-sourcemaps": "^0.4.2", "ts-node": "^8.3.0", @@ -64,10 +65,11 @@ "build": "run-s build:tsc build:rollup", "build:tsc": "tsc -p tsconfig.json", "build:rollup": "rollup -c rollup.config.js", + "format": "prettier --write \"./**/*.ts\"", "prepack": "npm install && npm run build", "test": "npm run build && run-p test:tslint test:unit", "test:tslint": "tslint -p . -c tslint.json", "test:unit": "mocha", "check:packagejsonversion": "ts-node ./.scripts/checkPackageJsonVersion.ts" } -} \ No newline at end of file +} diff --git a/samples/getCredentialsFromAzureCli.ts b/samples/getCredentialsFromAzureCli.ts index a4a630c..43ed5d3 100644 --- a/samples/getCredentialsFromAzureCli.ts +++ b/samples/getCredentialsFromAzureCli.ts @@ -16,16 +16,21 @@ async function listKeyVaultSecrets(creds: AzureCliCredentials): Promise { try { console.log(">>>>>>> KeyVault <<<<<<<<<<<"); const client = new ServiceClient(creds); - console.log(">>> Subscription associated with the access token: '%s'.", - creds.tokenInfo.subscription); + console.log( + ">>> Subscription associated with the access token: '%s'.", + creds.tokenInfo.subscription + ); const request: RequestPrepareOptions = { url: getKVUrl(keyvaultAccountName), method: "GET" }; console.log(">>> Request url: '%s'.", request.url); const res = await client.sendRequest(request); - console.log("List of secrets from keyvault account '%s': \n%O", - keyvaultAccountName, res.parsedBody); + console.log( + "List of secrets from keyvault account '%s': \n%O", + keyvaultAccountName, + res.parsedBody + ); } catch (err) { console.log(err); } @@ -40,29 +45,41 @@ async function listResourceGroups(creds: AzureCliCredentials): Promise { // Setting the resource to ARM endpoint. creds.resource = "https://management.azure.com"; const client = new ServiceClient(creds); - console.log(">>> Subscription associated with the access token: '%s'.", - creds.tokenInfo.subscription); + console.log( + ">>> Subscription associated with the access token: '%s'.", + creds.tokenInfo.subscription + ); const request: RequestPrepareOptions = { url: getUrl(creds.subscriptionInfo.id), method: "GET" }; console.log(">>> Request url: '%s'.", request.url); const res = await client.sendRequest(request); - console.log("List of resource groups from subscriptionId '%s': \n%O", - creds.subscriptionInfo.id, res.parsedBody); + console.log( + "List of resource groups from subscriptionId '%s': \n%O", + creds.subscriptionInfo.id, + res.parsedBody + ); // Let us change the subscriptionId, which should trigger refreshing the access token. const subscriptions = await AzureCliCredentials.listAllSubscriptions(); creds.subscriptionInfo = subscriptions[1]; - console.log(">>> The new subscription id associated with the credential object is: '%s'.", - creds.subscriptionInfo.id); + console.log( + ">>> The new subscription id associated with the credential object is: '%s'.", + creds.subscriptionInfo.id + ); request.url = getUrl(creds.subscriptionInfo.id); console.log(">>> Request url: '%s'.", request.url); const res2 = await client.sendRequest(request); - console.log("List of resource groups from subscriptionId '%s': \n%O", - creds.subscriptionInfo.id, res2.parsedBody); - console.log(">>> Subscription associated with the access token: '%s'.", - creds.tokenInfo.subscription); + console.log( + "List of resource groups from subscriptionId '%s': \n%O", + creds.subscriptionInfo.id, + res2.parsedBody + ); + console.log( + ">>> Subscription associated with the access token: '%s'.", + creds.tokenInfo.subscription + ); } catch (err) { console.log(err); } @@ -74,4 +91,4 @@ async function main() { await listResourceGroups(creds); } -main(); \ No newline at end of file +main(); diff --git a/samples/interactivePersonalAccount.ts b/samples/interactivePersonalAccount.ts index ae2e86b..0783f41 100644 --- a/samples/interactivePersonalAccount.ts +++ b/samples/interactivePersonalAccount.ts @@ -33,9 +33,7 @@ ${e} } // To get the tenants for your account, you can use the buildTenantList method: - const tenants = await msRestNodeAuth.buildTenantList( - authResponse.credentials - ); + const tenants = await msRestNodeAuth.buildTenantList(authResponse.credentials); // Update the domain used by the credentials so that it can generate tokens against a specific tenant. authResponse.credentials.setDomain(tenants[0]); diff --git a/test/credentials/msiAppServiceTokenCredentialTests.ts b/test/credentials/msiAppServiceTokenCredentialTests.ts index 4f7d769..ea7770f 100644 --- a/test/credentials/msiAppServiceTokenCredentialTests.ts +++ b/test/credentials/msiAppServiceTokenCredentialTests.ts @@ -7,7 +7,6 @@ import { expect, assert } from "chai"; import { WebResource, HttpHeaders, HttpClient, HttpOperationResponse } from "@azure/ms-rest-js"; describe("MSI App Service Authentication", function () { - function getMockHttpClient(response?: any, error?: any): HttpClient { const httpClient = { sendRequest: async (request: WebResource): Promise => { @@ -60,7 +59,6 @@ describe("MSI App Service Authentication", function () { token_type: "Bearer" }; - const httpClient = getMockHttpClient(mockResponse); process.env["MSI_ENDPOINT"] = "http://127.0.0.1:41741/MSI/token/"; process.env["MSI_SECRET"] = "69418689F1E342DD946CB82994CDA3CB"; @@ -73,8 +71,9 @@ describe("MSI App Service Authentication", function () { it('should throw if the response contains "ExceptionMessage"', async function () { const errorResponse = { - "error": "unknown", - "error_description": "ExceptionMessage: Failed to retrieve token from the Active directory. For details see logs in C:\\User1\\Logs\\Plugins\\Microsoft.Identity.MSI\\1.0\\service_identity_0.log" + error: "unknown", + error_description: + "ExceptionMessage: Failed to retrieve token from the Active directory. For details see logs in C:\\User1\\Logs\\Plugins\\Microsoft.Identity.MSI\\1.0\\service_identity_0.log" }; const httpClient = getMockHttpClient(undefined, errorResponse); @@ -84,15 +83,13 @@ describe("MSI App Service Authentication", function () { try { await msiCredsObj.getToken(); assert.fail(undefined, undefined, "getToken should throw an exception"); - } - catch (err) { + } catch (err) { expect(err); } }); }); describe("loginWithAppServiceMSI (callback)", () => { - it("should successfully provide MSIAppServiceTokenCredentials object by providing optional properties", (done) => { const mockResponse = { access_token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1d", @@ -139,8 +136,9 @@ describe("MSI App Service Authentication", function () { it('should throw if the response contains "ExceptionMessage"', async () => { const errorResponse = { - "error": "unknown", - "error_description": "ExceptionMessage: Failed to retrieve token from the Active directory. For details see logs in C:\\User1\\Logs\\Plugins\\Microsoft.Identity.MSI\\1.0\\service_identity_0.log" + error: "unknown", + error_description: + "ExceptionMessage: Failed to retrieve token from the Active directory. For details see logs in C:\\User1\\Logs\\Plugins\\Microsoft.Identity.MSI\\1.0\\service_identity_0.log" }; const httpClient = getMockHttpClient(undefined, errorResponse); @@ -155,4 +153,4 @@ describe("MSI App Service Authentication", function () { } }); }); -}); \ No newline at end of file +}); diff --git a/test/credentials/msiVmTokenCredentialTests.ts b/test/credentials/msiVmTokenCredentialTests.ts index 3a76d07..e3654bb 100644 --- a/test/credentials/msiVmTokenCredentialTests.ts +++ b/test/credentials/msiVmTokenCredentialTests.ts @@ -6,14 +6,23 @@ import { expect, assert } from "chai"; import { HttpClient, HttpOperationResponse, WebResource, HttpHeaders } from "@azure/ms-rest-js"; describe("MSI Vm Authentication", () => { - - function setupNockResponse(msiEndpoint?: string, expectedRequestHeaders?: any, response?: any, error?: any): HttpClient { + function setupNockResponse( + msiEndpoint?: string, + expectedRequestHeaders?: any, + response?: any, + error?: any + ): HttpClient { if (!msiEndpoint) { msiEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token"; } const isMatch = (actualRequest: WebResource, expectedRequestHeaders: any) => { - return actualRequest.url === `${msiEndpoint}?api-version=${expectedRequestHeaders.apiVersion}&resource=${encodeURIComponent(expectedRequestHeaders.resource)}`; + return ( + actualRequest.url === + `${msiEndpoint}?api-version=${ + expectedRequestHeaders.apiVersion + }&resource=${encodeURIComponent(expectedRequestHeaders.resource)}` + ); }; const httpClient = { @@ -47,8 +56,8 @@ describe("MSI Vm Authentication", () => { }; const expectedQuery = { - "apiVersion": "2018-02-01", - "resource": "https://management.azure.com/" + apiVersion: "2018-02-01", + resource: "https://management.azure.com/" }; const httpClient = setupNockResponse(undefined, expectedQuery, mockResponse); @@ -72,13 +81,16 @@ describe("MSI Vm Authentication", () => { }; const expectedQuery = { - "apiVersion": "2018-02-01", - "resource": "https://management.azure.com/" + apiVersion: "2018-02-01", + resource: "https://management.azure.com/" }; const customMsiEndpoint = "http://localhost:50342/oauth2/token"; const httpClient = setupNockResponse(customMsiEndpoint, expectedQuery, mockResponse); - const msiCredsObj = new MSIVmTokenCredentials({ msiEndpoint: customMsiEndpoint, httpClient: httpClient }); + const msiCredsObj = new MSIVmTokenCredentials({ + msiEndpoint: customMsiEndpoint, + httpClient: httpClient + }); const response = await msiCredsObj.getToken(); expect(response).to.exist; expect(response!.accessToken).to.exist; @@ -87,18 +99,19 @@ describe("MSI Vm Authentication", () => { it("should throw on requests with bad resource", async () => { const errorMessage = "unknown"; - const errorDescription = "Failed to retrieve token from the Active directory. For details see logs in C:\\User1\\Logs\\Plugins\\Microsoft.Identity.MSI\\1.0\\service_identity_0.log"; + const errorDescription = + "Failed to retrieve token from the Active directory. For details see logs in C:\\User1\\Logs\\Plugins\\Microsoft.Identity.MSI\\1.0\\service_identity_0.log"; const errorResponse = { - "error": errorMessage, - "error_description": errorDescription + error: errorMessage, + error_description: errorDescription }; const requestBodyToMatch = { - "resource": "badvalue" + resource: "badvalue" }; const httpClient = setupNockResponse(undefined, requestBodyToMatch, undefined, errorResponse); - const msiCredsObj = new MSIVmTokenCredentials({ "resource": "badvalue", httpClient: httpClient }); + const msiCredsObj = new MSIVmTokenCredentials({ resource: "badvalue", httpClient: httpClient }); try { await msiCredsObj.getToken(); @@ -114,16 +127,16 @@ describe("MSI Vm Authentication", () => { const errorMessage = "bad_resource_200"; const errorDescription = "Invalid Resource"; const errorResponse = { - "error": errorMessage, - "error_description": errorDescription + error: errorMessage, + error_description: errorDescription }; const requestBodyToMatch = { - "resource": " " + resource: " " }; const httpClient = setupNockResponse(undefined, requestBodyToMatch, undefined, errorResponse); - const msiCredsObj = new MSIVmTokenCredentials({ "resource": " ", httpClient: httpClient }); + const msiCredsObj = new MSIVmTokenCredentials({ resource: " ", httpClient: httpClient }); try { await msiCredsObj.getToken();