@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
JavaScript
"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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