text: "use strict";
  // Copyright (c) Microsoft Corporation.
  // 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.ParameterizedBotFrameworkAuthentication = void 0;
  const botframework_schema_1 = require("botframework-schema");
  const authenticationConstants_1 = require("./authenticationConstants");
  const authenticationError_1 = require("./authenticationError");
  const botFrameworkAuthentication_1 = require("./botFrameworkAuthentication");
  const connectorFactoryImpl_1 = require("./connectorFactoryImpl");
  const botFrameworkClientImpl_1 = require("./botFrameworkClientImpl");
  const claimsIdentity_1 = require("./claimsIdentity");
  const emulatorValidation_1 = require("./emulatorValidation");
  const jwtTokenExtractor_1 = require("./jwtTokenExtractor");
  const jwtTokenValidation_1 = require("./jwtTokenValidation");
  const skillValidation_1 = require("./skillValidation");
  const tokenValidationParameters_1 = require("./tokenValidationParameters");
  const userTokenClientImpl_1 = require("./userTokenClientImpl");
  function getAppId(claimsIdentity) {
      var _a, _b;
      // For requests from channel App Id is in Audience claim of JWT token. For emulator it is in AppId claim. For
      // unauthenticated requests we have anonymous claimsIdentity provided auth is disabled.
      // For Activities coming from Emulator AppId claim contains the Bot's AAD AppId.
      return ((_b = (_a = claimsIdentity.getClaimValue(authenticationConstants_1.AuthenticationConstants.AudienceClaim)) !== null && _a !== void 0 ? _a : claimsIdentity.getClaimValue(authenticationConstants_1.AuthenticationConstants.AppIdClaim)) !== null && _b !== void 0 ? _b : undefined);
  }
  // Internal
  class ParameterizedBotFrameworkAuthentication extends botFrameworkAuthentication_1.BotFrameworkAuthentication {
      constructor(validateAuthority, toChannelFromBotLoginUrl, toChannelFromBotOAuthScope, toBotFromChannelTokenIssuer, oAuthUrl, toBotFromChannelOpenIdMetadataUrl, toBotFromEmulatorOpenIdMetadataUrl, callerId, credentialsFactory, authConfiguration, botFrameworkClientFetch, connectorClientOptions = {}) {
          super();
          this.validateAuthority = validateAuthority;
          this.toChannelFromBotLoginUrl = toChannelFromBotLoginUrl;
          this.toChannelFromBotOAuthScope = toChannelFromBotOAuthScope;
          this.toBotFromChannelTokenIssuer = toBotFromChannelTokenIssuer;
          this.oAuthUrl = oAuthUrl;
          this.toBotFromChannelOpenIdMetadataUrl = toBotFromChannelOpenIdMetadataUrl;
          this.toBotFromEmulatorOpenIdMetadataUrl = toBotFromEmulatorOpenIdMetadataUrl;
          this.callerId = callerId;
          this.credentialsFactory = credentialsFactory;
          this.authConfiguration = authConfiguration;
          this.botFrameworkClientFetch = botFrameworkClientFetch;
          this.connectorClientOptions = connectorClientOptions;
      }
      getOriginatingAudience() {
          return this.toChannelFromBotOAuthScope;
      }
      authenticateChannelRequest(authHeader) {
          return __awaiter(this, void 0, void 0, function* () {
              if (!authHeader.trim()) {
                  const isAuthDisabled = yield this.credentialsFactory.isAuthenticationDisabled();
                  if (!isAuthDisabled) {
                      throw new authenticationError_1.AuthenticationError('Unauthorized Access. Request is not authorized', botframework_schema_1.StatusCodes.UNAUTHORIZED);
                  }
                  // 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. Since ChannelServiceHandler calls are
                  // always a skill callback call, we set the skill claim too.
                  return skillValidation_1.SkillValidation.createAnonymousSkillClaim();
              }
              return this.JwtTokenValidation_validateAuthHeader(authHeader, 'unknown', null);
          });
      }
      authenticateRequest(activity, authHeader) {
          return __awaiter(this, void 0, void 0, function* () {
              const claimsIdentity = yield this.JwtTokenValidation_authenticateRequest(activity, authHeader);
              const outboundAudience = skillValidation_1.SkillValidation.isSkillClaim(claimsIdentity.claims)
                  ? jwtTokenValidation_1.JwtTokenValidation.getAppIdFromClaims(claimsIdentity.claims)
                  : this.toChannelFromBotOAuthScope;
              const callerId = yield this.generateCallerId(this.credentialsFactory, claimsIdentity, this.callerId);
              const connectorFactory = new connectorFactoryImpl_1.ConnectorFactoryImpl(getAppId(claimsIdentity), this.toChannelFromBotOAuthScope, this.toChannelFromBotLoginUrl, this.validateAuthority, this.credentialsFactory);
              return {
                  audience: outboundAudience,
                  callerId,
                  claimsIdentity,
                  connectorFactory,
              };
          });
      }
      authenticateStreamingRequest(authHeader, channelIdHeader) {
          return __awaiter(this, void 0, void 0, function* () {
              if (!(channelIdHeader === null || channelIdHeader === void 0 ? void 0 : channelIdHeader.trim()) && !(yield this.credentialsFactory.isAuthenticationDisabled())) {
                  throw new authenticationError_1.AuthenticationError("'channelIdHeader' required.", botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              const claimsIdentity = yield this.JwtTokenValidation_validateAuthHeader(authHeader, channelIdHeader, null);
              const outboundAudience = skillValidation_1.SkillValidation.isSkillClaim(claimsIdentity.claims)
                  ? jwtTokenValidation_1.JwtTokenValidation.getAppIdFromClaims(claimsIdentity.claims)
                  : this.toChannelFromBotOAuthScope;
              const callerId = yield this.generateCallerId(this.credentialsFactory, claimsIdentity, this.callerId);
              return { audience: outboundAudience, callerId, claimsIdentity };
          });
      }
      createUserTokenClient(claimsIdentity) {
          return __awaiter(this, void 0, void 0, function* () {
              const appId = getAppId(claimsIdentity);
              const credentials = yield this.credentialsFactory.createCredentials(appId, this.toChannelFromBotOAuthScope, this.toChannelFromBotLoginUrl, this.validateAuthority);
              return new userTokenClientImpl_1.UserTokenClientImpl(appId, credentials, this.oAuthUrl, this.connectorClientOptions);
          });
      }
      createConnectorFactory(claimsIdentity) {
          return new connectorFactoryImpl_1.ConnectorFactoryImpl(getAppId(claimsIdentity), this.toChannelFromBotOAuthScope, this.toChannelFromBotLoginUrl, this.validateAuthority, this.credentialsFactory);
      }
      createBotFrameworkClient() {
          return new botFrameworkClientImpl_1.BotFrameworkClientImpl(this.credentialsFactory, this.toChannelFromBotLoginUrl, this.botFrameworkClientFetch);
      }
      JwtTokenValidation_authenticateRequest(activity, authHeader) {
          var _a;
          return __awaiter(this, void 0, void 0, function* () {
              if (!authHeader.trim()) {
                  const isAuthDisabled = yield this.credentialsFactory.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 && ((_a = activity.recipient) === null || _a === void 0 ? void 0 : _a.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 this.JwtTokenValidation_validateAuthHeader(authHeader, activity.channelId, activity.serviceUrl);
              return claimsIdentity;
          });
      }
      JwtTokenValidation_validateAuthHeader(authHeader, channelId, serviceUrl = '') {
          return __awaiter(this, void 0, void 0, function* () {
              const identity = yield this.JwtTokenValidation_authenticateToken(authHeader, channelId, serviceUrl);
              yield this.JwtTokenValidation_validateClaims(identity.claims);
              return identity;
          });
      }
      JwtTokenValidation_validateClaims(claims = []) {
          return __awaiter(this, void 0, void 0, function* () {
              if (this.authConfiguration.validateClaims) {
                  // Call the validation method if defined (it should throw an exception if the validation fails)
                  yield this.authConfiguration.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);
              }
          });
      }
      JwtTokenValidation_authenticateToken(authHeader, channelId, serviceUrl) {
          return __awaiter(this, void 0, void 0, function* () {
              if (skillValidation_1.SkillValidation.isSkillToken(authHeader)) {
                  return this.SkillValidation_authenticateChannelToken(authHeader, channelId);
              }
              if (emulatorValidation_1.EmulatorValidation.isTokenFromEmulator(authHeader)) {
                  return this.EmulatorValidation_authenticateEmulatorToken(authHeader, channelId);
              }
              // Handle requests from BotFramework Channels
              return this.ChannelValidation_authenticateChannelToken(authHeader, serviceUrl, channelId);
          });
      }
      SkillValidation_authenticateChannelToken(authHeader, channelId) {
          return __awaiter(this, void 0, void 0, function* () {
              const tokenExtractor = new jwtTokenExtractor_1.JwtTokenExtractor(tokenValidationParameters_1.ToBotFromBotOrEmulatorTokenValidationParameters, this.toBotFromEmulatorOpenIdMetadataUrl, authenticationConstants_1.AuthenticationConstants.AllowedSigningAlgorithms);
              const parts = authHeader.split(' ');
              const identity = yield tokenExtractor.getIdentity(parts[0], parts[1], channelId, this.authConfiguration.requiredEndorsements);
              yield this.SkillValidation_ValidateIdentity(identity);
              return identity;
          });
      }
      SkillValidation_ValidateIdentity(identity) {
          return __awaiter(this, void 0, void 0, function* () {
              if (!identity) {
                  // No valid identity. Not Authorized.
                  throw new authenticationError_1.AuthenticationError('SkillValidation.validateIdentity(): Invalid identity', botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              if (!identity.isAuthenticated) {
                  // The token is in some way invalid. Not Authorized.
                  throw new authenticationError_1.AuthenticationError('SkillValidation.validateIdentity(): Token not authenticated', botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              const versionClaim = identity.getClaimValue(authenticationConstants_1.AuthenticationConstants.VersionClaim);
              if (!versionClaim) {
                  // No version claim
                  throw new authenticationError_1.AuthenticationError(`SkillValidation.validateIdentity(): '${authenticationConstants_1.AuthenticationConstants.VersionClaim}' claim is required on skill Tokens.`, botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              // Look for the "aud" claim, but only if issued from the Bot Framework
              const audienceClaim = identity.getClaimValue(authenticationConstants_1.AuthenticationConstants.AudienceClaim);
              if (!audienceClaim) {
                  // Claim is not present or doesn't have a value. Not Authorized.
                  throw new authenticationError_1.AuthenticationError(`SkillValidation.validateIdentity(): '${authenticationConstants_1.AuthenticationConstants.AudienceClaim}' claim is required on skill Tokens.`, botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              if (!(yield this.credentialsFactory.isValidAppId(audienceClaim))) {
                  // The AppId is not valid. Not Authorized.
                  throw new authenticationError_1.AuthenticationError('SkillValidation.validateIdentity(): Invalid audience.', botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              const appId = jwtTokenValidation_1.JwtTokenValidation.getAppIdFromClaims(identity.claims);
              if (!appId) {
                  // Invalid appId
                  throw new authenticationError_1.AuthenticationError('SkillValidation.validateIdentity(): Invalid appId.', botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
          });
      }
      EmulatorValidation_authenticateEmulatorToken(authHeader, channelId) {
          return __awaiter(this, void 0, void 0, function* () {
              const tokenExtractor = new jwtTokenExtractor_1.JwtTokenExtractor(tokenValidationParameters_1.ToBotFromBotOrEmulatorTokenValidationParameters, this.toBotFromEmulatorOpenIdMetadataUrl, authenticationConstants_1.AuthenticationConstants.AllowedSigningAlgorithms);
              const identity = yield tokenExtractor.getIdentityFromAuthHeader(authHeader, channelId, this.authConfiguration.requiredEndorsements);
              if (!identity) {
                  // No valid identity. Not Authorized.
                  throw new authenticationError_1.AuthenticationError('Unauthorized. No valid identity.', botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              if (!identity.isAuthenticated) {
                  // The token is in some way invalid. Not Authorized.
                  throw new authenticationError_1.AuthenticationError('Unauthorized. Is not authenticated', botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              // Now check that the AppID in the claimset matches
              // what we're looking for. Note that in a multi-tenant bot, this value
              // comes from developer code that may be reaching out to a service, hence the
              // Async validation.
              const versionClaim = identity.getClaimValue(authenticationConstants_1.AuthenticationConstants.VersionClaim);
              if (versionClaim === null) {
                  throw new authenticationError_1.AuthenticationError('Unauthorized. "ver" claim is required on Emulator Tokens.', botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              let appId = '';
              // The Emulator, depending on Version, sends the AppId via either the
              // appid claim (Version 1) or the Authorized Party claim (Version 2).
              if (!versionClaim || versionClaim === '1.0') {
                  // either no Version or a version of "1.0" means we should look for
                  // the claim in the "appid" claim.
                  const appIdClaim = identity.getClaimValue(authenticationConstants_1.AuthenticationConstants.AppIdClaim);
                  if (!appIdClaim) {
                      // No claim around AppID. Not Authorized.
                      throw new authenticationError_1.AuthenticationError('Unauthorized. "appid" claim is required on Emulator Token version "1.0".', botframework_schema_1.StatusCodes.UNAUTHORIZED);
                  }
                  appId = appIdClaim;
              }
              else if (versionClaim === '2.0') {
                  // Emulator, "2.0" puts the AppId in the "azp" claim.
                  const appZClaim = identity.getClaimValue(authenticationConstants_1.AuthenticationConstants.AuthorizedParty);
                  if (!appZClaim) {
                      // No claim around AppID. Not Authorized.
                      throw new authenticationError_1.AuthenticationError('Unauthorized. "azp" claim is required on Emulator Token version "2.0".', botframework_schema_1.StatusCodes.UNAUTHORIZED);
                  }
                  appId = appZClaim;
              }
              else {
                  // Unknown Version. Not Authorized.
                  throw new authenticationError_1.AuthenticationError(`Unauthorized. Unknown Emulator Token version "${versionClaim}".`, botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              if (!(yield this.credentialsFactory.isValidAppId(appId))) {
                  throw new authenticationError_1.AuthenticationError(`Unauthorized. Invalid AppId passed on token: ${appId}`, botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              return identity;
          });
      }
      ChannelValidation_authenticateChannelToken(authHeader, serviceUrl, channelId) {
          return __awaiter(this, void 0, void 0, function* () {
              const tokenValidationParameters = this.ChannelValidation_GetTokenValidationParameters();
              const tokenExtractor = new jwtTokenExtractor_1.JwtTokenExtractor(tokenValidationParameters, this.toBotFromChannelOpenIdMetadataUrl, authenticationConstants_1.AuthenticationConstants.AllowedSigningAlgorithms);
              const identity = yield tokenExtractor.getIdentityFromAuthHeader(authHeader, channelId, this.authConfiguration.requiredEndorsements);
              return this.governmentChannelValidation_ValidateIdentity(identity, serviceUrl);
          });
      }
      ChannelValidation_GetTokenValidationParameters() {
          return {
              issuer: [this.toBotFromChannelTokenIssuer],
              audience: undefined,
              clockTolerance: 5 * 60,
              ignoreExpiration: false,
          };
      }
      governmentChannelValidation_ValidateIdentity(identity, serviceUrl) {
          return __awaiter(this, void 0, void 0, function* () {
              if (!identity) {
                  // No valid identity. Not Authorized.
                  throw new authenticationError_1.AuthenticationError('Unauthorized. No valid identity.', botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              if (!identity.isAuthenticated) {
                  // The token is in some way invalid. Not Authorized.
                  throw new authenticationError_1.AuthenticationError('Unauthorized. Is not authenticated', botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              // Now check that the AppID in the claimset matches
              // what we're looking for. Note that in a multi-tenant bot, this value
              // comes from developer code that may be reaching out to a service, hence the
              // Async validation.
              // Look for the "aud" claim, but only if issued from the Bot Framework
              if (identity.getClaimValue(authenticationConstants_1.AuthenticationConstants.IssuerClaim) !== this.toBotFromChannelTokenIssuer) {
                  // The relevant Audiance Claim MUST be present. Not Authorized.
                  throw new authenticationError_1.AuthenticationError('Unauthorized. Issuer Claim MUST be present.', botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              // The AppId from the claim in the token must match the AppId specified by the developer.
              // In this case, the token is destined for the app, so we find the app ID in the audience claim.
              const audClaim = identity.getClaimValue(authenticationConstants_1.AuthenticationConstants.AudienceClaim);
              if (!(yield this.credentialsFactory.isValidAppId(audClaim || ''))) {
                  // The AppId is not valid or not present. Not Authorized.
                  throw new authenticationError_1.AuthenticationError(`Unauthorized. Invalid AppId passed on token: ${audClaim}`, botframework_schema_1.StatusCodes.UNAUTHORIZED);
              }
              if (serviceUrl) {
                  const serviceUrlClaim = identity.getClaimValue(authenticationConstants_1.AuthenticationConstants.ServiceUrlClaim);
                  if (serviceUrlClaim !== serviceUrl) {
                      // Claim must match. Not Authorized.
                      throw new authenticationError_1.AuthenticationError('Unauthorized. ServiceUrl claim do not match.', botframework_schema_1.StatusCodes.UNAUTHORIZED);
                  }
              }
              return identity;
          });
      }
  }
  exports.ParameterizedBotFrameworkAuthentication = ParameterizedBotFrameworkAuthentication;
  //# sourceMappingURL=parameterizedBotFrameworkAuthentication.js.map