UNPKG

@microsoft/teams.apps

Version:

<p> <a href="https://www.npmjs.com/package/@microsoft/teams.apps" target="_blank"> <img src="https://img.shields.io/npm/v/@microsoft/teams.apps/latest" /> </a> <a href="https://www.npmjs.com/package/@microsoft/teams.apps?activeTab=code

419 lines • 35.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TokenManager = exports.DEFAULT_TENANT_FOR_GRAPH_TOKEN = void 0; const msal_node_1 = require("@azure/msal-node"); const teams_api_1 = require("@microsoft/teams.api"); const teams_common_1 = require("@microsoft/teams.common"); /** * Graph falls back to the multi-tenant `common` endpoint when no tenant is * known, unlike other scopes which fall back to the cloud's login tenant. */ exports.DEFAULT_TENANT_FOR_GRAPH_TOKEN = 'common'; const TOKEN_EXCHANGE_SCOPE = 'api://AzureADTokenExchange/.default'; const MSAL_LOG_LEVEL_TO_LOG_LEVEL = { [msal_node_1.LogLevel.Error]: 'error', [msal_node_1.LogLevel.Warning]: 'warn', [msal_node_1.LogLevel.Info]: 'info', [msal_node_1.LogLevel.Verbose]: 'debug', [msal_node_1.LogLevel.Trace]: 'trace' }; const LOG_LEVEL_TO_MSAL_LOG_LEVEL = { 'error': msal_node_1.LogLevel.Error, 'warn': msal_node_1.LogLevel.Warning, 'info': msal_node_1.LogLevel.Info, 'debug': msal_node_1.LogLevel.Verbose, 'trace': msal_node_1.LogLevel.Trace }; // Type guard functions function isClientCredentials(credentials) { return 'clientSecret' in credentials; } function isTokenCredentials(credentials) { return 'token' in credentials; } function isFederatedIdentityCredentials(credentials) { return 'managedIdentityType' in credentials; } class TokenManager { credentials; logger; _msalLogger; cloud; confidentialClientsByTenantId = {}; federatedIdentityClientsByTenantId = {}; agenticAppClientsByTenantAndAppId = {}; managedIdentityClient = null; constructor(options, logger) { this.logger = logger.child('TokenManager') ?? new teams_common_1.ConsoleLogger('TokenManager'); this._msalLogger = this.logger.child('azure/msal-node', { // Msal logging is fairly noisy. So we keep it quiet unless the user // explicitly turns it on pattern: '-azure/msal-node' }); this.cloud = options.cloud ?? teams_api_1.PUBLIC; this.credentials = this.initializeCredentials(options); } async getBotToken() { return await this.getToken(this.cloud.botScope, this.resolveTenantId(undefined, this.cloud.loginTenant)); } async getAppToken(scope, tenantId) { return await this.getToken(scope, this.resolveTenantId(tenantId, this.cloud.loginTenant)); } async getGraphToken(tenantId) { return await this.getToken(this.cloud.graphScope, this.resolveTenantId(tenantId, exports.DEFAULT_TENANT_FOR_GRAPH_TOKEN)); } /** * Acquires an Agentic User-scoped token. * * @param scope the scope to request the final token for. * @param agenticAppId the agentic app ID that owns the user. * @param agenticUserId the agentic user ID to act as. * @param tenantId the tenant to acquire the token in. Defaults to the tenant * configured on the credentials. * @returns the token, or `null` when the app has no credentials configured. */ async getAgenticUserToken(scope, agenticAppId, agenticUserId, tenantId) { if (!this.credentials) { return null; } const resolvedAgenticAppId = this.requireAgenticAppId(agenticAppId, 'AgenticUser'); const resolvedAgenticUserId = this.requireAgenticUserId(agenticUserId); const resolvedTenantId = this.resolveAgenticTenantId(tenantId, 'AgenticUser'); if (isTokenCredentials(this.credentials)) { const getAgenticUserToken = this.requireTokenProviderCapability(this.credentials, 'getAgenticUserToken', 'AgenticUser'); return this.toProviderToken(await getAgenticUserToken(scope, resolvedAgenticAppId, resolvedAgenticUserId, resolvedTenantId)); } this.requireClientCredentials(this.credentials, 'AgenticUser'); const blueprintAssertion = this.blueprintAssertionFor(resolvedAgenticAppId, resolvedTenantId); // Rung 2: an app token, requested for the exchange scope so it can // be presented as the subject assertion for the user exchange below. const { client, token: appToken } = await this.acquireAgenticAppToken(TOKEN_EXCHANGE_SCOPE, resolvedAgenticAppId, resolvedTenantId, blueprintAssertion); // Rung 3: redeem the app token for an Agentic User token. const userResult = await client.acquireTokenByUserFederatedIdentityCredential({ scopes: [scope], assertion: appToken, userObjectId: resolvedAgenticUserId, clientAssertion: await blueprintAssertion(), }); return new teams_api_1.JsonWebToken(this.getAccessTokenOrThrow(userResult, 'Agent token exchange step 3 failed')); } /** * Acquires an agentic app-scoped token. * * @param scope the scope to request the final token for. * @param agenticAppId the agentic app ID to act as. * @param tenantId the tenant to acquire the token in. Defaults to the tenant * configured on the credentials. * @returns the token, or `null` when the app has no credentials configured. */ async getAgenticAppToken(scope, agenticAppId, tenantId) { if (!this.credentials) { return null; } const resolvedAgenticAppId = this.requireAgenticAppId(agenticAppId, 'AgenticApp'); const resolvedTenantId = this.resolveAgenticTenantId(tenantId, 'AgenticApp'); if (isTokenCredentials(this.credentials)) { const getAgenticAppToken = this.requireTokenProviderCapability(this.credentials, 'getAgenticAppToken', 'AgenticApp'); return this.toProviderToken(await getAgenticAppToken(scope, resolvedAgenticAppId, resolvedTenantId)); } this.requireClientCredentials(this.credentials, 'AgenticApp'); const { token } = await this.acquireAgenticAppToken(scope, resolvedAgenticAppId, resolvedTenantId, this.blueprintAssertionFor(resolvedAgenticAppId, resolvedTenantId)); return new teams_api_1.JsonWebToken(token); } /** * Rung 1 of the agentic token ladder: an ordinary confidential-client grant * for the app's own credentials, requesting the token exchange scope with an * `fmiPath` naming the agentic app. * * Returned as a callback rather than a token because MSAL resolves client * assertions lazily and re-invokes them when the assertion expires. */ blueprintAssertionFor(agenticAppId, tenantId) { return async () => { const confidentialClient = this.getConfidentialClient(this.credentials, tenantId); const result = await confidentialClient.acquireTokenByClientCredential({ scopes: [TOKEN_EXCHANGE_SCOPE], fmiPath: agenticAppId, }); return this.getAccessTokenOrThrow(result, 'Agent token exchange step 1 failed'); }; } /** * Rung 2 of the agentic token ladder: a client-credentials grant made *as the * agentic app*, authenticated by the rung 1 blueprint assertion. * * Also returns the MSAL client so callers that need to climb to rung 3 can * reuse it instead of re-resolving it. */ async acquireAgenticAppToken(scope, agenticAppId, tenantId, blueprintAssertion) { const client = this.getAgenticAppClient(tenantId, agenticAppId, blueprintAssertion); const result = await client.acquireTokenByClientCredential({ scopes: [scope] }); return { client, token: this.getAccessTokenOrThrow(result, 'Agent token exchange step 2 failed'), }; } requireAgenticAppId(agenticAppId, tokenLabel) { if (!agenticAppId) { throw new Error(`agenticAppId is required to get an ${tokenLabel} token`); } return agenticAppId; } requireAgenticUserId(agenticUserId) { if (!agenticUserId) { throw new Error('agenticUserId is required to get an AgenticUser token'); } return agenticUserId; } resolveAgenticTenantId(tenantId, tokenLabel) { const resolved = tenantId ?? this.credentials?.tenantId; if (!resolved) { throw new Error(`tenantId is required to get an ${tokenLabel} token`); } return resolved; } requireClientCredentials(credentials, tokenLabel) { if (!isClientCredentials(credentials)) { throw new Error(`${tokenLabel} tokens require ClientCredentials`); } return credentials; } initializeCredentials(options) { const clientId = options.clientId ?? process.env.CLIENT_ID; const tenantId = options.tenantId ?? process.env.TENANT_ID; const clientSecret = options.clientSecret ?? process.env.CLIENT_SECRET; const token = options.token; const managedIdentityClientId = options.managedIdentityClientId ?? process.env.MANAGED_IDENTITY_CLIENT_ID; if (clientId && clientSecret) { this.logger.debug('Using Client Credentials auth'); return { clientId, clientSecret, tenantId, }; } else if (clientId && token) { this.logger.debug(('Using custom token factory auth')); return { clientId, tenantId, token, }; } else if (clientId && !clientSecret) { if (managedIdentityClientId == null || managedIdentityClientId.toLowerCase() === clientId.toLowerCase()) { this.logger.debug('Using user managed identity auth'); return { clientId, tenantId }; } else { const identityType = managedIdentityClientId === 'system' ? 'system' : 'user'; this.logger.debug(`Using Federated Identity Credentials auth (${identityType})`); return { clientId, tenantId, managedIdentityClientId, managedIdentityType: identityType, }; } } return undefined; } async getToken(scope, tenantId) { if (!this.credentials) { return null; } if (isClientCredentials(this.credentials)) { return this.getTokenWithClientCredentials(this.credentials, scope, tenantId); } else if (isTokenCredentials(this.credentials)) { return this.getTokenWithTokenProvider(this.credentials, scope, tenantId); } else if (isFederatedIdentityCredentials(this.credentials)) { return this.getTokenWithFederatedCredentials(this.credentials, scope, tenantId); } else { return this.getTokenWithManagedIdentity(this.credentials, scope); } } async getTokenWithClientCredentials(credentials, scope, tenantId) { const confidentialClient = this.getConfidentialClient(credentials, tenantId); const result = await confidentialClient.acquireTokenByClientCredential({ scopes: [scope] }); return this.handleTokenResponse(result); } async getTokenWithTokenProvider(credentials, scope, tenantId) { const provider = credentials.token; const token = typeof provider === 'function' ? await provider(scope, tenantId) : await provider.getAppToken(scope, tenantId); return this.toProviderToken(token); } /** * Normalizes whatever a token provider returned into an `IToken`. `null` / * `undefined` pass through as `null`. */ toProviderToken(value) { if (value == null) { return null; } return new teams_api_1.JsonWebToken(typeof value === 'string' ? value : value.toString()); } /** * Resolves a capability off a custom token provider, or throws when it is * missing rather than falling back under an identity the caller did not ask for. */ requireTokenProviderCapability(credentials, capability, tokenLabel) { const provider = credentials.token; const method = typeof provider === 'function' ? undefined : provider[capability]; if (!method) { throw new Error(`${tokenLabel} tokens require a token provider implementing \`${capability}\`. ` + 'The configured `token` credential does not, and returning an app-only token instead ' + 'would authenticate as the app under the wrong identity.'); } return method.bind(provider); } async getTokenWithManagedIdentity(credentials, scope) { const managedIdentityClient = this.getManagedIdentityClient(credentials); // Resource doesn't need the ./default suffix const resource = scope.replace('/.default', ''); const result = await managedIdentityClient.acquireToken({ resource }); return this.handleTokenResponse(result); } async getTokenWithFederatedCredentials(credentials, scope, tenantId) { const confidentialClient = this.getFederatedIdentityClient(credentials, tenantId); const result = await confidentialClient.acquireTokenByClientCredential({ scopes: [scope] }); return this.handleTokenResponse(result); } resolveTenantId(tenantId, defaultTenantId) { return tenantId || this.credentials?.tenantId || defaultTenantId; } getConfidentialClient(credentials, tenantId) { const cachedClient = this.confidentialClientsByTenantId[tenantId]; if (cachedClient) { return cachedClient; } const client = new msal_node_1.ConfidentialClientApplication({ auth: { clientId: credentials.clientId, clientSecret: credentials.clientSecret, authority: `${this.cloud.loginEndpoint}/${tenantId}` }, system: { loggerOptions: this.buildLoggerOptions() } }); this.confidentialClientsByTenantId[tenantId] = client; return client; } getFederatedIdentityClient(credentials, tenantId) { const cachedClient = this.federatedIdentityClientsByTenantId[tenantId]; if (cachedClient) { return cachedClient; } const client = new msal_node_1.ConfidentialClientApplication({ auth: { clientId: credentials.clientId, clientAssertion: async () => { const managedIdentityTokenRes = await this.getManagedIdentityClient(credentials).acquireToken({ resource: 'api://AzureADTokenExchange' }); if (!managedIdentityTokenRes) { throw new Error('Failed to acquire managed identity exchange token for federated identity credential'); } return managedIdentityTokenRes.accessToken; }, authority: `${this.cloud.loginEndpoint}/${tenantId}` }, system: { loggerOptions: this.buildLoggerOptions() } }); this.federatedIdentityClientsByTenantId[tenantId] = client; return client; } getAgenticAppClient(tenantId, agenticAppId, clientAssertion) { const cacheKey = `${tenantId}:${agenticAppId}`; const cachedClient = this.agenticAppClientsByTenantAndAppId[cacheKey]; if (cachedClient) { return cachedClient; } const client = new msal_node_1.ConfidentialClientApplication({ auth: { clientId: agenticAppId, clientAssertion, authority: `${this.cloud.loginEndpoint}/${tenantId}` }, system: { loggerOptions: this.buildLoggerOptions() } }); this.agenticAppClientsByTenantAndAppId[cacheKey] = client; return client; } getManagedIdentityClient(credentials) { if (this.managedIdentityClient) { return this.managedIdentityClient; } if (isFederatedIdentityCredentials(credentials)) { if (credentials.managedIdentityType === 'user') { this.managedIdentityClient = new msal_node_1.ManagedIdentityApplication({ managedIdentityIdParams: { userAssignedClientId: credentials.managedIdentityClientId }, system: { loggerOptions: this.buildLoggerOptions() } }); } else { this.managedIdentityClient = new msal_node_1.ManagedIdentityApplication({ managedIdentityIdParams: undefined, //no options automatically indicates system assigned managed identity system: { loggerOptions: this.buildLoggerOptions() } }); } } else { this.managedIdentityClient = new msal_node_1.ManagedIdentityApplication({ managedIdentityIdParams: { userAssignedClientId: credentials.clientId }, system: { loggerOptions: this.buildLoggerOptions() } }); } return this.managedIdentityClient; } handleTokenResponse(result) { if (!result) { throw new Error('Failed to get token'); } return new teams_api_1.JsonWebToken(result.accessToken); } getAccessTokenOrThrow(result, errorPrefix) { if (!result?.accessToken) { throw new Error(`${errorPrefix}: Failed to get token`); } return result.accessToken; } buildLoggerOptions() { return { logLevel: this.logger.loggerOptions?.level != null ? LOG_LEVEL_TO_MSAL_LOG_LEVEL[this.logger.loggerOptions.level] : undefined, loggerCallback: (level, message) => { // There's a bug in MSAL where it warns this on all requests. // https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/7917 if (message.endsWith('Warning - No client info in response')) { return; } this._msalLogger.log(MSAL_LOG_LEVEL_TO_LOG_LEVEL[level], message); }, piiLoggingEnabled: false, }; } } exports.TokenManager = TokenManager; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW4tbWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy90b2tlbi1tYW5hZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLGdEQUFnSztBQUVoSyxvREFBMFA7QUFDMVAsMERBQTJFO0FBRTNFOzs7R0FHRztBQUNVLFFBQUEsOEJBQThCLEdBQUcsUUFBUSxDQUFDO0FBQ3ZELE1BQU0sb0JBQW9CLEdBQUcscUNBQXFDLENBQUM7QUFFbkUsTUFBTSwyQkFBMkIsR0FBbUM7SUFDbEUsQ0FBQyxvQkFBWSxDQUFDLEtBQUssQ0FBQyxFQUFFLE9BQU87SUFDN0IsQ0FBQyxvQkFBWSxDQUFDLE9BQU8sQ0FBQyxFQUFFLE1BQU07SUFDOUIsQ0FBQyxvQkFBWSxDQUFDLElBQUksQ0FBQyxFQUFFLE1BQU07SUFDM0IsQ0FBQyxvQkFBWSxDQUFDLE9BQU8sQ0FBQyxFQUFFLE9BQU87SUFDL0IsQ0FBQyxvQkFBWSxDQUFDLEtBQUssQ0FBQyxFQUFFLE9BQU87Q0FDOUIsQ0FBQztBQUNGLE1BQU0sMkJBQTJCLEdBQW1DO0lBQ2xFLE9BQU8sRUFBRSxvQkFBWSxDQUFDLEtBQUs7SUFDM0IsTUFBTSxFQUFFLG9CQUFZLENBQUMsT0FBTztJQUM1QixNQUFNLEVBQUUsb0JBQVksQ0FBQyxJQUFJO0lBQ3pCLE9BQU8sRUFBRSxvQkFBWSxDQUFDLE9BQU87SUFDN0IsT0FBTyxFQUFFLG9CQUFZLENBQUMsS0FBSztDQUM1QixDQUFDO0FBSUYsdUJBQXVCO0FBQ3ZCLFNBQVMsbUJBQW1CLENBQUMsV0FBd0I7SUFDbkQsT0FBTyxjQUFjLElBQUksV0FBVyxDQUFDO0FBQ3ZDLENBQUM7QUFFRCxTQUFTLGtCQUFrQixDQUFDLFdBQXdCO0lBQ2xELE9BQU8sT0FBTyxJQUFJLFdBQVcsQ0FBQztBQUNoQyxDQUFDO0FBRUQsU0FBUyw4QkFBOEIsQ0FBQyxXQUF3QjtJQUM5RCxPQUFPLHFCQUFxQixJQUFJLFdBQVcsQ0FBQztBQUM5QyxDQUFD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