botframework-connector
Version:
Bot Connector is autorest generated connector client.
232 lines • 13.2 kB
JavaScript
;
/**
* @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