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

133 lines • 11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InboundActivityTokenValidator = exports.AGENT_365_PLATFORM_APP_ID = void 0; const teams_api_1 = require("@microsoft/teams.api"); const jwt_validator_1 = require("./jwt-validator"); const MAX_ENTRA_VALIDATOR_CACHE_SIZE = 100; const ENTRA_V1_ISSUER_PREFIX = 'https://sts.windows.net/'; /** * App ID of the Agent 365 platform. */ exports.AGENT_365_PLATFORM_APP_ID = '5a807f24-c9de-44ee-a3a7-329e88a00ffc'; /** * Derives the JWKS keys URI from an OpenID metadata URL. * e.g. "https://login.botframework.com/v1/.well-known/openidconfiguration" * -> "https://login.botframework.com/v1/.well-known/keys" */ function openIdMetadataToKeysUri(openIdMetadataUrl) { return openIdMetadataUrl.replace(/\/openidconfiguration$/, '/keys'); } /** * Validator for inbound Teams activities. * * Classic bot activities use Bot Framework connector tokens. AgenticIdentity * activities use Entra tokens whose audience is the Agentic App Blueprint ID. */ class InboundActivityTokenValidator { jwtValidator; entraValidatorsByTenantId = new Map(); credentials; appId; cloud; logger; constructor(appId, tenantId, serviceUrl, logger, cloud) { const env = cloud ?? teams_api_1.PUBLIC; this.appId = appId; this.cloud = env; this.logger = logger; this.jwtValidator = new jwt_validator_1.JwtValidator({ clientId: appId, tenantId, loginEndpoint: env.loginEndpoint, validateIssuer: { allowedIssuer: env.tokenIssuer }, validateServiceUrl: serviceUrl ? { expectedServiceUrl: serviceUrl } : undefined, jwksUriOptions: { type: 'uri', uri: openIdMetadataToKeysUri(env.openIdMetadataUrl), }, }, logger); this.credentials = { clientId: appId, tenantId }; } async check(authHeader, body) { // Extract token from "Bearer <token>" format const token = authHeader.startsWith('Bearer ') ? authHeader.substring(7) : authHeader; if (!token) { throw new Error('No token provided'); } const unverifiedPayload = this.decodePayload(token); const isEntraToken = this.isEntraIssuer(unverifiedPayload?.iss); const payload = isEntraToken ? await this.validateEntraToken(token, unverifiedPayload) : await this.jwtValidator.validateAccessToken(token, { validateServiceUrl: body.serviceUrl ? { expectedServiceUrl: body.serviceUrl } : undefined }); if (!payload) { throw new Error('Invalid token'); } const serviceUrl = body.serviceUrl || payload.serviceurl || ''; // Convert JWT payload to IToken return { appId: payload.appid || this.credentials?.clientId || '', from: 'azure', fromId: payload.sub || '', serviceUrl, isExpired: () => false, // Already validated by JWT validator }; } decodePayload(rawToken) { return (0, jwt_validator_1.decodeJwtPayload)(rawToken); } isEntraIssuer(issuer) { return typeof issuer === 'string' && (issuer.startsWith(this.cloud.loginEndpoint) || issuer.startsWith(ENTRA_V1_ISSUER_PREFIX)); } async validateEntraToken(rawToken, unverifiedPayload) { const tenantId = unverifiedPayload?.tid; if (!tenantId || typeof tenantId !== 'string') { throw new Error('Entra inbound token is missing tid'); } const validator = this.getEntraValidator(tenantId); // AgenticIdentity inbound Entra tokens currently do not include serviceurl. // Revisit service URL validation when the platform defines a signed claim. const payload = await validator.validateAccessToken(rawToken); if (payload) { this.validateEntraCallerApp(payload); } return payload; } /** * Requires the client app that requested the token to be the Agent 365 platform. * Entra v2 tokens carry the caller in `azp` and v1 tokens carry it in `appid`. * `appid` is only consulted when `azp` is absent, so a present but invalid `azp` is rejected. */ validateEntraCallerApp(payload) { const callerAppId = 'azp' in payload ? payload.azp : payload.appid; if (typeof callerAppId !== 'string' || callerAppId.toLowerCase() !== exports.AGENT_365_PLATFORM_APP_ID) { throw new Error('Entra inbound token caller app is not allowed'); } } getEntraValidator(tenantId) { const cachedValidator = this.entraValidatorsByTenantId.get(tenantId); if (cachedValidator) { return cachedValidator; } const validator = new jwt_validator_1.JwtValidator({ clientId: this.appId, tenantId, loginEndpoint: this.cloud.loginEndpoint, validateIssuer: { allowedTenantIds: [tenantId] }, jwksUriOptions: { type: 'tenantId' }, }, this.logger); this.entraValidatorsByTenantId.set(tenantId, validator); if (this.entraValidatorsByTenantId.size > MAX_ENTRA_VALIDATOR_CACHE_SIZE) { const oldestTenantId = this.entraValidatorsByTenantId.keys().next().value; if (oldestTenantId) { this.entraValidatorsByTenantId.delete(oldestTenantId); } } return validator; } } exports.InboundActivityTokenValidator = InboundActivityTokenValidator; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5ib3VuZC1hY3Rpdml0eS10b2tlbi12YWxpZGF0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbWlkZGxld2FyZS9hdXRoL2luYm91bmQtYWN0aXZpdHktdG9rZW4tdmFsaWRhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBLG9EQUFxRjtBQUdyRixtREFBaUU7QUFFakUsTUFBTSw4QkFBOEIsR0FBRyxHQUFHLENBQUM7QUFDM0MsTUFBTSxzQkFBc0IsR0FBRywwQkFBMEIsQ0FBQztBQUUxRDs7R0FFRztBQUNVLFFBQUEseUJBQXlCLEdBQUcsc0NBQXNDLENBQUM7QUFFaEY7Ozs7R0FJRztBQUNILFNBQVMsdUJBQXVCLENBQUMsaUJBQXlCO0lBQ3hELE9BQU8saUJBQWlCLENBQUMsT0FBTyxDQUFDLHdCQUF3QixFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQ3RFLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILE1BQWEsNkJBQTZCO0lBQ2hDLFlBQVksQ0FBZTtJQUMzQix5QkFBeUIsR0FBRyxJQUFJLEdBQUcsRUFBd0IsQ0FBQztJQUM1RCxXQUFXLENBQWU7SUFDMUIsS0FBSyxDQUFTO0lBQ2QsS0FBSyxDQUFtQjtJQUN4QixNQUFNLENBQVc7SUFFekIsWUFDRSxLQUFhLEVBQ2IsUUFBaUIsRUFDakIsVUFBbUIsRUFDbkIsTUFBZ0IsRUFDaEIsS0FBd0I7UUFFeEIsTUFBTSxHQUFHLEdBQUcsS0FBSyxJQUFJLGtCQUFNLENBQUM7UUFDNUIsSUFBSSxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7UUFDbkIsSUFBSSxDQUFDLEtBQUssR0FBRyxHQUFHLENBQUM7UUFDakIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLDRCQUFZLENBQUM7WUFDbkMsUUFBUSxFQUFFLEtBQUs7WUFDZixRQUFRO1lBQ1IsYUFBYSxFQUFFLEdBQUcsQ0FBQyxhQUFhO1lB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