UNPKG

botframework-connector

Version:

Bot Connector is autorest generated connector client.

215 lines (190 loc) 7.43 kB
/** * @module botframework-connector */ /** * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ import { ConfidentialClientApplication } from '@azure/msal-node'; import { ServiceClientCredentials, WebResourceLike as WebResource } from 'botbuilder-stdlib/lib/azureCoreHttpCompat'; import { TokenCredentials } from './tokenCredentials'; import { AuthenticationConstants } from './authenticationConstants'; import { AuthenticatorResult } from './authenticatorResult'; /** * General AppCredentials auth implementation and cache. * Subclasses can implement refreshToken to acquire the token. */ export abstract class AppCredentials implements ServiceClientCredentials { private static readonly cache: Map<string, AuthenticatorResult> = new Map<string, AuthenticatorResult>(); appId: string; private _oAuthEndpoint: string; private _oAuthScope: string; private _tenant: string; tokenCacheKey: string; protected clientApplication: ConfidentialClientApplication; // Protects against JSON.stringify leaking secrets private toJSON(): unknown { return { name: this.constructor.name, appId: this.appId, tenant: this.tenant, oAuthEndpoint: this.oAuthEndpoint, oAuthScope: this.oAuthScope, }; } /** * Initializes a new instance of the [AppCredentials](xref:botframework-connector.AppCredentials) class. * * @param appId The App ID. * @param channelAuthTenant Tenant ID of the Azure AD tenant where the bot is created. * - Required for SingleTenant app types. * - Optional for MultiTenant app types. **Note**: '_botframework.com_' is the default tenant when no value is provided. * * More information: https://learn.microsoft.com/en-us/security/zero-trust/develop/identity-supported-account-types. * @param oAuthScope The scope for the token. */ constructor(appId: string, channelAuthTenant?: string, oAuthScope: string = null) { this.appId = appId; this.tenant = channelAuthTenant; this.oAuthEndpoint = this.GetToChannelFromBotLoginUrlPrefix() + this.tenant; this.oAuthScope = oAuthScope && oAuthScope.length > 0 ? oAuthScope : this.GetToChannelFromBotOAuthScope(); } /** * Gets tenant to be used for channel authentication. * * @returns The channel auth token tenant for this credential. */ private get tenant(): string { return this._tenant; } /** * Sets tenant to be used for channel authentication. */ private set tenant(value: string) { this._tenant = value && value.length > 0 ? value : this.GetDefaultChannelAuthTenant(); } /** * Gets the OAuth scope to use. * * @returns The OAuth scope to use. */ get oAuthScope(): string { return this._oAuthScope; } /** * Sets the OAuth scope to use. */ set oAuthScope(value: string) { this._oAuthScope = value; this.tokenCacheKey = `${this.appId}${this.oAuthScope}-cache`; } /** * Gets the OAuth endpoint to use. * * @returns The OAuthEndpoint to use. */ get oAuthEndpoint(): string { return this._oAuthEndpoint; } /** * Sets the OAuth endpoint to use. */ set oAuthEndpoint(value: string) { // aadApiVersion is set to '1.5' to avoid the "spn:" concatenation on the audience claim // For more info, see https://github.com/AzureAD/azure-activedirectory-library-for-nodejs/issues/128 this._oAuthEndpoint = value; } /** * Adds the host of service url to trusted hosts. * If expiration time is not provided, the expiration date will be current (utc) date + 1 day. * * @deprecated * * @param {string} serviceUrl The service url * @param {Date} expiration? The expiration date after which this service url is not trusted anymore */ static trustServiceUrl(serviceUrl: string, expiration?: Date): void; /** * Adds the host of service url to trusted hosts. * If expiration time is not provided, the expiration date will be current (utc) date + 1 day. */ static trustServiceUrl(): void { // no-op } /** * Checks if the service url is for a trusted host or not. * * @deprecated * * @param {string} serviceUrl The service url * @returns {boolean} True if the host of the service url is trusted; False otherwise. */ static isTrustedServiceUrl(serviceUrl: string): boolean; /** * Checks if the service url is for a trusted host or not. * * @returns {boolean} True if the host of the service url is trusted; False otherwise. */ static isTrustedServiceUrl(): boolean { return true; } /** * Apply the credentials to the HTTP request. * * @param webResource The WebResource HTTP request. * @returns A Promise representing the asynchronous operation. */ async signRequest(webResource: WebResource): Promise<WebResource> { if (this.shouldSetToken()) { return new TokenCredentials(await this.getToken()).signRequest(webResource); } return webResource; } /** * Gets an OAuth access token. * * @param forceRefresh True to force a refresh of the token; or false to get * a cached token if it exists. * @returns A Promise that represents the work queued to execute. * @remarks If the promise is successful, the result contains the access token string. */ async getToken(forceRefresh = false): Promise<string> { if (!forceRefresh) { // check the global cache for the token. If we have it, and it's valid, we're done. const oAuthToken = AppCredentials.cache.get(this.tokenCacheKey); // Check if the token is not expired. if (oAuthToken && oAuthToken.expiresOn > new Date()) { return oAuthToken.accessToken; } } // We need to refresh the token, because: // 1. The user requested it via the forceRefresh parameter // 2. We have it, but it's expired // 3. We don't have it in the cache. const res = await this.refreshToken(); if (res && res.accessToken) { // Subtract 5 minutes from expiresOn so they'll we'll get a new token before it expires. res.expiresOn.setMinutes(res.expiresOn.getMinutes() - 5); AppCredentials.cache.set(this.tokenCacheKey, res); return res.accessToken; } else { throw new Error('Authentication: No response or error received from MSAL.'); } } protected GetToChannelFromBotOAuthScope(): string { return AuthenticationConstants.ToChannelFromBotOAuthScope; } protected GetToChannelFromBotLoginUrlPrefix(): string { return AuthenticationConstants.ToChannelFromBotLoginUrlPrefix; } protected GetDefaultChannelAuthTenant(): string { return AuthenticationConstants.DefaultChannelAuthTenant; } protected abstract refreshToken(): Promise<AuthenticatorResult>; /** * @private */ private shouldSetToken(): boolean { return this.appId && this.appId !== AuthenticationConstants.AnonymousSkillAppId; } }