UNPKG

botframework-connector

Version:

Bot Connector is autorest generated connector client.

232 lines 13.2 kB
"use strict"; /** * @module botframework-connector */ /** * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.JwtTokenValidation = void 0; const botframework_schema_1 = require("botframework-schema"); const authenticationError_1 = require("./authenticationError"); const authenticationConfiguration_1 = require("./authenticationConfiguration"); const authenticationConstants_1 = require("./authenticationConstants"); const channelValidation_1 = require("./channelValidation"); const claimsIdentity_1 = require("./claimsIdentity"); const emulatorValidation_1 = require("./emulatorValidation"); const enterpriseChannelValidation_1 = require("./enterpriseChannelValidation"); const governmentChannelValidation_1 = require("./governmentChannelValidation"); const governmentConstants_1 = require("./governmentConstants"); const skillValidation_1 = require("./skillValidation"); const aseChannelValidation_1 = require("./aseChannelValidation"); /** * @deprecated Use `ConfigurationBotFrameworkAuthentication` instead to perform JWT token validation. */ // eslint-disable-next-line @typescript-eslint/no-namespace var JwtTokenValidation; (function (JwtTokenValidation) { /** * Authenticates the request and sets the service url in the set of trusted urls. * * @param {Partial<Activity>} activity The incoming Activity from the Bot Framework or the Emulator * @param {string} authHeader The Bearer token included as part of the request * @param {ICredentialProvider} credentials The set of valid credentials, such as the Bot Application ID * @param {string} channelService The channel service * @param {AuthenticationConfiguration} authConfig Optional, the auth config * @returns {Promise<ClaimsIdentity>} Promise with ClaimsIdentity for the request. */ function authenticateRequest(activity, authHeader, credentials, channelService, authConfig) { return __awaiter(this, void 0, void 0, function* () { if (!authConfig) { authConfig = new authenticationConfiguration_1.AuthenticationConfiguration(); } // eslint-disable-next-line prettier/prettier if (!authHeader.trim()) { // CodeQL [SM01513] We manually validate incoming tokens. Checking for empty header as part of that. const isAuthDisabled = yield credentials.isAuthenticationDisabled(); if (!isAuthDisabled) { throw new authenticationError_1.AuthenticationError('Unauthorized Access. Request is not authorized', botframework_schema_1.StatusCodes.UNAUTHORIZED); } // Check if the activity is for a skill call and is coming from the Emulator. if (activity.channelId === botframework_schema_1.Channels.Emulator && activity.recipient && activity.recipient.role === botframework_schema_1.RoleTypes.Skill) { return skillValidation_1.SkillValidation.createAnonymousSkillClaim(); } // In the scenario where Auth is disabled, we still want to have the // IsAuthenticated flag set in the ClaimsIdentity. To do this requires // adding in an empty claim. return new claimsIdentity_1.ClaimsIdentity([], authenticationConstants_1.AuthenticationConstants.AnonymousAuthType); } const claimsIdentity = yield validateAuthHeader(authHeader, credentials, channelService, activity.channelId, activity.serviceUrl, authConfig); return claimsIdentity; }); } JwtTokenValidation.authenticateRequest = authenticateRequest; /** * Validate an auth header. * * @param {string} authHeader the auth header * @param {ICredentialProvider} credentials the credentials * @param {string} channelService the channel service * @param {string} channelId the channel ID * @param {string} serviceUrl the service URL * @param {AuthenticationConfiguration} authConfig the auth config * @returns {Promise<ClaimsIdentity>} a promise that resolves to the validated claims, or rejects if validation fails */ function validateAuthHeader(authHeader, credentials, channelService, channelId, serviceUrl = '', authConfig = new authenticationConfiguration_1.AuthenticationConfiguration()) { return __awaiter(this, void 0, void 0, function* () { if (!authHeader.trim()) { throw new authenticationError_1.AuthenticationError("'authHeader' required.", botframework_schema_1.StatusCodes.BAD_REQUEST); } const identity = yield authenticateToken(authHeader, credentials, channelService, channelId, authConfig, serviceUrl); yield validateClaims(authConfig, identity.claims); return identity; }); } JwtTokenValidation.validateAuthHeader = validateAuthHeader; function authenticateToken(authHeader, credentials, channelService, channelId, authConfig, serviceUrl) { return __awaiter(this, void 0, void 0, function* () { if (aseChannelValidation_1.AseChannelValidation.isTokenFromAseChannel(channelId)) { return aseChannelValidation_1.AseChannelValidation.authenticateAseChannelToken(authHeader); } if (skillValidation_1.SkillValidation.isSkillToken(authHeader)) { return yield skillValidation_1.SkillValidation.authenticateChannelToken(authHeader, credentials, channelService, channelId, authConfig); } if (emulatorValidation_1.EmulatorValidation.isTokenFromEmulator(authHeader)) { return yield emulatorValidation_1.EmulatorValidation.authenticateEmulatorToken(authHeader, credentials, channelService, channelId); } if (isPublicAzure(channelService)) { // eslint-disable-next-line prettier/prettier if (serviceUrl.trim()) { // CodeQL [SM01513] We manually validate incoming tokens. Checking for empty serviceUrl as part of that. return yield channelValidation_1.ChannelValidation.authenticateChannelTokenWithServiceUrl(authHeader, credentials, serviceUrl, channelId); } return yield channelValidation_1.ChannelValidation.authenticateChannelToken(authHeader, credentials, channelId); } if (isGovernment(channelService)) { // eslint-disable-next-line prettier/prettier if (serviceUrl.trim()) { // CodeQL [SM01513] We manually validate incoming tokens. Checking for empty serviceUrl as part of that. return yield governmentChannelValidation_1.GovernmentChannelValidation.authenticateChannelTokenWithServiceUrl(authHeader, credentials, serviceUrl, channelId); } return yield governmentChannelValidation_1.GovernmentChannelValidation.authenticateChannelToken(authHeader, credentials, channelId); } // Otherwise use Enterprise Channel Validation // eslint-disable-next-line prettier/prettier if (serviceUrl.trim()) { // CodeQL [SM01513] We manually validate incoming tokens. Checking for empty serviceUrl as part of that. return yield enterpriseChannelValidation_1.EnterpriseChannelValidation.authenticateChannelTokenWithServiceUrl(authHeader, credentials, serviceUrl, channelId, channelService); } return yield enterpriseChannelValidation_1.EnterpriseChannelValidation.authenticateChannelToken(authHeader, credentials, channelId, channelService); }); } /** * Validates the identity claims against the ClaimsValidator in AuthenticationConfiguration if present. * * @param authConfig The authentication configuration. * @param claims The list of claims to validate. */ function validateClaims(authConfig, claims = []) { return __awaiter(this, void 0, void 0, function* () { if (authConfig.validateClaims) { // Call the validation method if defined (it should throw an exception if the validation fails) yield authConfig.validateClaims(claims); } else if (skillValidation_1.SkillValidation.isSkillClaim(claims)) { // Skill claims must be validated using AuthenticationConfiguration validateClaims throw new authenticationError_1.AuthenticationError('Unauthorized Access. Request is not authorized. Skill Claims require validation.', botframework_schema_1.StatusCodes.UNAUTHORIZED); } }); } /** * Gets the AppId from a claims list. * * @summary * In v1 tokens the AppId is in the "ver" AuthenticationConstants.AppIdClaim claim. * In v2 tokens the AppId is in the "azp" AuthenticationConstants.AuthorizedParty claim. * If the AuthenticationConstants.VersionClaim is not present, this method will attempt to * obtain the attribute from the AuthenticationConstants.AppIdClaim or if present. * * Throws a TypeError if claims is falsy. * * @param {Claim[]} claims An object containing claims types and their values. * @returns {string} the app ID */ function getAppIdFromClaims(claims) { if (!claims) { throw new TypeError('JwtTokenValidation.getAppIdFromClaims(): missing claims.'); } let appId; // Group claims by type for fast lookup const claimsByType = claims.reduce((acc, claim) => (Object.assign(Object.assign({}, acc), { [claim.type]: claim })), {}); // Depending on Version, the AppId is either in the // appid claim (Version 1) or the 'azp' claim (Version 2). const versionClaim = claimsByType[authenticationConstants_1.AuthenticationConstants.VersionClaim]; const versionValue = versionClaim && versionClaim.value; if (!versionValue || versionValue === '1.0') { // No version or a version of '1.0' means we should look for // the claim in the 'appid' claim. const appIdClaim = claimsByType[authenticationConstants_1.AuthenticationConstants.AppIdClaim]; if (appIdClaim && appIdClaim.value) { appId = appIdClaim.value; } } else if (versionValue === '2.0') { // Version '2.0' puts the AppId in the 'azp' claim. const azpClaim = claimsByType[authenticationConstants_1.AuthenticationConstants.AuthorizedParty]; if (azpClaim && azpClaim.value) { appId = azpClaim.value; } } return appId; } JwtTokenValidation.getAppIdFromClaims = getAppIdFromClaims; function isPublicAzure(channelService) { return !channelService || channelService.length === 0; } /** * Determine whether or not a channel service is government * * @param {string} channelService the channel service * @returns {boolean} true if this is a government channel service */ function isGovernment(channelService) { return channelService && channelService.toLowerCase() === governmentConstants_1.GovernmentConstants.ChannelService; } JwtTokenValidation.isGovernment = isGovernment; /** * Internal helper to check if the token has the shape we expect "Bearer [big long string]". * * @param {string} authHeader A string containing the token header. * @returns {boolean} True if the token is valid, false if not. */ function isValidTokenFormat(authHeader) { if (!authHeader) { // No token, not valid. return false; } const parts = authHeader.trim().split(' '); if (parts.length !== 2) { // Tokens MUST have exactly 2 parts. If we don't have 2 parts, it's not a valid token return false; } // We now have an array that should be: // [0] = "Bearer" // [1] = "[Big Long String]" const authScheme = parts[0]; if (authScheme !== 'Bearer') { // The scheme MUST be "Bearer" return false; } return true; } JwtTokenValidation.isValidTokenFormat = isValidTokenFormat; })(JwtTokenValidation = exports.JwtTokenValidation || (exports.JwtTokenValidation = {})); //# sourceMappingURL=jwtTokenValidation.js.map