diff --git a/libraries/botbuilder-core/src/configurationBotFrameworkAuthentication.ts b/libraries/botbuilder-core/src/configurationBotFrameworkAuthentication.ts index b13b4174f5..f80d6a62e4 100644 --- a/libraries/botbuilder-core/src/configurationBotFrameworkAuthentication.ts +++ b/libraries/botbuilder-core/src/configurationBotFrameworkAuthentication.ts @@ -17,6 +17,7 @@ import { ConnectorFactory, ServiceClientCredentialsFactory, UserTokenClient, + AseChannelValidation, } from 'botframework-connector'; import { @@ -26,6 +27,16 @@ import { const TypedOptions = z .object({ + /** + * The ID assigned to your bot in the [Bot Framework Portal](https://dev.botframework.com/). + */ + MicrosoftAppId: z.string(), + + /** + * The tenant id assigned to your bot in the [Bot Framework Portal](https://dev.botframework.com/). + */ + MicrosoftAppTenantId: z.string(), + /** * (Optional) The OAuth URL used to get a token from OAuthApiClient. The "OAuthUrl" member takes precedence over this value. */ @@ -131,6 +142,7 @@ export class ConfigurationBotFrameworkAuthentication extends BotFrameworkAuthent super(); try { + AseChannelValidation.init(botFrameworkAuthConfig); const typedBotFrameworkAuthConfig = TypedOptions.nonstrict().parse(botFrameworkAuthConfig); const { diff --git a/libraries/botbuilder/src/botFrameworkAdapter.ts b/libraries/botbuilder/src/botFrameworkAdapter.ts index cbe01ab5f7..7df7cc2599 100644 --- a/libraries/botbuilder/src/botFrameworkAdapter.ts +++ b/libraries/botbuilder/src/botFrameworkAdapter.ts @@ -56,6 +56,7 @@ import { GovernmentConstants, JwtTokenValidation, MicrosoftAppCredentials, + MicrosoftGovernmentAppCredentials, SignInUrlResponse, SimpleCredentialProvider, SkillValidation, @@ -254,11 +255,19 @@ export class BotFrameworkAdapter ); this.credentialsProvider = new SimpleCredentialProvider(this.credentials.appId, ''); } else { - this.credentials = new MicrosoftAppCredentials( - this.settings.appId, - this.settings.appPassword || '', - this.settings.channelAuthTenant - ); + if (JwtTokenValidation.isGovernment(this.settings.channelService)) { + this.credentials = new MicrosoftGovernmentAppCredentials( + this.settings.appId, + this.settings.appPassword || '', + this.settings.channelAuthTenant + ); + } else { + this.credentials = new MicrosoftAppCredentials( + this.settings.appId, + this.settings.appPassword || '', + this.settings.channelAuthTenant + ); + } this.credentialsProvider = new SimpleCredentialProvider( this.credentials.appId, this.settings.appPassword || '' @@ -280,10 +289,6 @@ export class BotFrameworkAdapter ChannelValidation.OpenIdMetadataEndpoint = this.settings.openIdMetadata; GovernmentChannelValidation.OpenIdMetadataEndpoint = this.settings.openIdMetadata; } - if (JwtTokenValidation.isGovernment(this.settings.channelService)) { - this.credentials.oAuthEndpoint = GovernmentConstants.ToChannelFromBotLoginUrl; - this.credentials.oAuthScope = GovernmentConstants.ToChannelFromBotOAuthScope; - } // If a NodeWebSocketFactoryBase was passed in, set it on the BotFrameworkAdapter. if (this.settings.webSocketFactory) { @@ -1627,12 +1632,21 @@ export class BotFrameworkAdapter this.settings.channelAuthTenant ); } else { - credentials = new MicrosoftAppCredentials(appId, appPassword, this.settings.channelAuthTenant, oAuthScope); - } - - if (JwtTokenValidation.isGovernment(this.settings.channelService)) { - credentials.oAuthEndpoint = GovernmentConstants.ToChannelFromBotLoginUrl; - credentials.oAuthScope = oAuthScope || GovernmentConstants.ToChannelFromBotOAuthScope; + if (JwtTokenValidation.isGovernment(this.settings.channelService)) { + credentials = new MicrosoftGovernmentAppCredentials( + appId, + appPassword, + this.settings.channelAuthTenant, + oAuthScope + ); + } else { + credentials = new MicrosoftAppCredentials( + appId, + appPassword, + this.settings.channelAuthTenant, + oAuthScope + ); + } } return credentials; diff --git a/libraries/botbuilder/src/botFrameworkHttpClient.ts b/libraries/botbuilder/src/botFrameworkHttpClient.ts index fc2adebebe..1586cf64f5 100644 --- a/libraries/botbuilder/src/botFrameworkHttpClient.ts +++ b/libraries/botbuilder/src/botFrameworkHttpClient.ts @@ -13,10 +13,10 @@ import { AppCredentials, AuthenticationConstants, ConversationConstants, - GovernmentConstants, ICredentialProvider, JwtTokenValidation, MicrosoftAppCredentials, + MicrosoftGovernmentAppCredentials, } from 'botframework-connector'; import { USER_AGENT } from './botFrameworkAdapter'; @@ -158,9 +158,7 @@ export class BotFrameworkHttpClient implements BotFrameworkClient { protected async buildCredentials(appId: string, oAuthScope?: string): Promise { const appPassword = await this.credentialProvider.getAppPassword(appId); if (JwtTokenValidation.isGovernment(this.channelService)) { - const appCredentials = new MicrosoftAppCredentials(appId, appPassword, undefined, oAuthScope); - appCredentials.oAuthEndpoint = GovernmentConstants.ToChannelFromBotLoginUrl; - return appCredentials; + return new MicrosoftGovernmentAppCredentials(appId, appPassword, undefined, oAuthScope); } else { return new MicrosoftAppCredentials(appId, appPassword, undefined, oAuthScope); } diff --git a/libraries/botframework-connector/src/auth/appCredentials.ts b/libraries/botframework-connector/src/auth/appCredentials.ts index 4df398456f..7e3a82d30b 100644 --- a/libraries/botframework-connector/src/auth/appCredentials.ts +++ b/libraries/botframework-connector/src/auth/appCredentials.ts @@ -45,15 +45,11 @@ export abstract class AppCredentials implements ServiceClientCredentials { * @param channelAuthTenant Optional. The oauth token tenant. * @param oAuthScope The scope for the token. */ - constructor( - appId: string, - channelAuthTenant?: string, - oAuthScope: string = AuthenticationConstants.ToBotFromChannelTokenIssuer - ) { + constructor(appId: string, channelAuthTenant?: string, oAuthScope: string = null) { this.appId = appId; this.tenant = channelAuthTenant; - this.oAuthEndpoint = AuthenticationConstants.ToChannelFromBotLoginUrlPrefix + this.tenant; - this.oAuthScope = oAuthScope; + this.oAuthEndpoint = this.GetToChannelFromBotLoginUrlPrefix() + this.tenant; + this.oAuthScope = oAuthScope && oAuthScope.length > 0 ? oAuthScope : this.GetToChannelFromBotOAuthScope(); } /** @@ -69,7 +65,7 @@ export abstract class AppCredentials implements ServiceClientCredentials { * Sets tenant to be used for channel authentication. */ private set tenant(value: string) { - this._tenant = value && value.length > 0 ? value : AuthenticationConstants.DefaultChannelAuthTenant; + this._tenant = value && value.length > 0 ? value : this.GetDefaultChannelAuthTenant(); } /** @@ -191,6 +187,18 @@ export abstract class AppCredentials implements ServiceClientCredentials { } } + protected GetToChannelFromBotOAuthScope(): string { + return AuthenticationConstants.ToChannelFromBotOAuthScope; + } + + protected GetToChannelFromBotLoginUrlPrefix(): string { + return AuthenticationConstants.ToChannelFromBotLoginUrlPrefix; + } + + protected GetDefaultChannelAuthTenant(): string { + return AuthenticationConstants.DefaultChannelAuthTenant; + } + protected abstract refreshToken(): Promise; /** diff --git a/libraries/botframework-connector/src/auth/aseChannelValidation.ts b/libraries/botframework-connector/src/auth/aseChannelValidation.ts new file mode 100644 index 0000000000..4d326e6279 --- /dev/null +++ b/libraries/botframework-connector/src/auth/aseChannelValidation.ts @@ -0,0 +1,164 @@ +/** + * @module botframework-connector + */ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +/* eslint-disable @typescript-eslint/no-namespace */ + +import { ClaimsIdentity } from './claimsIdentity'; +import { AuthenticationConstants } from './authenticationConstants'; +import { AuthenticationConfiguration } from './authenticationConfiguration'; +import { GovernmentConstants } from './governmentConstants'; +import { ICredentialProvider } from './credentialProvider'; +import { JwtTokenExtractor } from './jwtTokenExtractor'; +import { JwtTokenValidation } from './jwtTokenValidation'; +import { AuthenticationError } from './authenticationError'; +import { SimpleCredentialProvider } from './credentialProvider'; +import { StatusCodes } from 'botframework-schema'; +import { BetweenBotAndAseChannelTokenValidationParameters } from './tokenValidationParameters'; + +/** + * @deprecated Use `ConfigurationBotFrameworkAuthentication` instead to perform AseChannel validation. + * Validates and Examines JWT tokens from the Bot Framework AseChannel + */ +export namespace AseChannelValidation { + const ChannelId = 'AseChannel'; + let _creadentialProvider: ICredentialProvider; + let _channelService: string; + export let MetadataUrl: string; + + /** + * init authentication from user .env configuration. + * + * @param configuration The user .env configuration. + */ + export function init(configuration: any) { + const appId = configuration.MicrosoftAppId; + const tenantId = configuration.MicrosoftAppTenantId; + _channelService = configuration.ChannelService; + MetadataUrl = + _channelService !== undefined && JwtTokenValidation.isGovernment(_channelService) + ? GovernmentConstants.ToBotFromEmulatorOpenIdMetadataUrl + : AuthenticationConstants.ToBotFromEmulatorOpenIdMetadataUrl; + + _creadentialProvider = new SimpleCredentialProvider(appId, ''); + + const tenantIds: string[] = [ + tenantId, + 'f8cdef31-a31e-4b4a-93e4-5f571e91255a', // US Gov MicrosoftServices.onmicrosoft.us + 'd6d49420-f39b-4df7-a1dc-d59a935871db', // Public botframework.com + ]; + const validIssuers: string[] = []; + tenantIds.forEach((tmpId: string) => { + validIssuers.push(`https://sts.windows.net/${tmpId}/`); // Auth Public/US Gov, 1.0 token + validIssuers.push(`https://login.microsoftonline.com/${tmpId}/v2.0`); // Auth Public, 2.0 token + validIssuers.push(`https://login.microsoftonline.us/${tmpId}/v2.0`); // Auth for US Gov, 2.0 token + }); + BetweenBotAndAseChannelTokenValidationParameters.issuer = validIssuers; + } + + /** + * Determines if a given Auth header is from the Bot Framework AseChannel + * + * @param {string} channelId The channelId. + * @returns {boolean} True, if the token was issued by the AseChannel. Otherwise, false. + */ + export function isTokenFromAseChannel(channelId: string): boolean { + return channelId === ChannelId; + } + + /** + * Validate the incoming Auth Header as a token sent from the Bot Framework AseChannel. + * A token issued by the Bot Framework will FAIL this check. Only AseChannel tokens will pass. + * + * @param {string} authHeader The raw HTTP header in the format: 'Bearer [longString]' + * @param {AuthenticationConfiguration} authConfig The authentication configuration. + * @returns {Promise} A valid ClaimsIdentity. + */ + export async function authenticateAseChannelToken( + authHeader: string, + authConfig: AuthenticationConfiguration = new AuthenticationConfiguration() + ): Promise { + const tokenExtractor: JwtTokenExtractor = new JwtTokenExtractor( + BetweenBotAndAseChannelTokenValidationParameters, + MetadataUrl, + AuthenticationConstants.AllowedSigningAlgorithms + ); + + const identity: ClaimsIdentity = await tokenExtractor.getIdentityFromAuthHeader( + authHeader, + ChannelId, + authConfig.requiredEndorsements + ); + if (!identity) { + // No valid identity. Not Authorized. + throw new AuthenticationError('Unauthorized. No valid identity.', StatusCodes.UNAUTHORIZED); + } + + if (!identity.isAuthenticated) { + // The token is in some way invalid. Not Authorized. + throw new AuthenticationError('Unauthorized. Is not authenticated', 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: string = identity.getClaimValue(AuthenticationConstants.VersionClaim); + if (versionClaim === null) { + throw new AuthenticationError( + 'Unauthorized. "ver" claim is required on Emulator Tokens.', + 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: string = identity.getClaimValue(AuthenticationConstants.AppIdClaim); + if (!appIdClaim) { + // No claim around AppID. Not Authorized. + throw new AuthenticationError( + 'Unauthorized. "appid" claim is required on Emulator Token version "1.0".', + StatusCodes.UNAUTHORIZED + ); + } + + appId = appIdClaim; + } else if (versionClaim === '2.0') { + // Emulator, "2.0" puts the AppId in the "azp" claim. + const appZClaim: string = identity.getClaimValue(AuthenticationConstants.AuthorizedParty); + if (!appZClaim) { + // No claim around AppID. Not Authorized. + throw new AuthenticationError( + 'Unauthorized. "azp" claim is required on Emulator Token version "2.0".', + StatusCodes.UNAUTHORIZED + ); + } + + appId = appZClaim; + } else { + // Unknown Version. Not Authorized. + throw new AuthenticationError( + `Unauthorized. Unknown Emulator Token version "${versionClaim}".`, + StatusCodes.UNAUTHORIZED + ); + } + + if (!(await _creadentialProvider.isValidAppId(appId))) { + throw new AuthenticationError( + `Unauthorized. Invalid AppId passed on token: ${appId}`, + StatusCodes.UNAUTHORIZED + ); + } + + return identity; + } +} diff --git a/libraries/botframework-connector/src/auth/governmentConstants.ts b/libraries/botframework-connector/src/auth/governmentConstants.ts index 4a422bcd7a..dc873237f6 100644 --- a/libraries/botframework-connector/src/auth/governmentConstants.ts +++ b/libraries/botframework-connector/src/auth/governmentConstants.ts @@ -15,9 +15,21 @@ export namespace GovernmentConstants { /** * TO CHANNEL FROM BOT: Login URL + * + * DEPRECATED: DO NOT USE */ export const ToChannelFromBotLoginUrl = 'https://login.microsoftonline.us/MicrosoftServices.onmicrosoft.us'; + /** + * TO CHANNEL FROM BOT: Login URL prefix + */ + export const ToChannelFromBotLoginUrlPrefix = 'https://login.microsoftonline.us/'; + + /** + * TO CHANNEL FROM BOT: Default tenant from which to obtain a token for bot to channel communication + */ + export const DefaultChannelAuthTenant = 'MicrosoftServices.onmicrosoft.us'; + /** * TO CHANNEL FROM BOT: OAuth scope to request */ diff --git a/libraries/botframework-connector/src/auth/index.ts b/libraries/botframework-connector/src/auth/index.ts index 38a5cb890e..5367b217b3 100644 --- a/libraries/botframework-connector/src/auth/index.ts +++ b/libraries/botframework-connector/src/auth/index.ts @@ -22,6 +22,7 @@ export * from './claimsIdentity'; export * from './connectorFactory'; export * from './credentialProvider'; export * from './emulatorValidation'; +export * from './aseChannelValidation'; export * from './endorsementsValidator'; export * from './enterpriseChannelValidation'; export * from './governmentChannelValidation'; @@ -32,9 +33,11 @@ export * from './managedIdentityAppCredentials'; export * from './managedIdentityAuthenticator'; export * from './managedIdentityServiceClientCredentialsFactory'; export * from './microsoftAppCredentials'; +export * from './microsoftGovernmentAppCredentials'; export * from './passwordServiceClientCredentialFactory'; export * from './serviceClientCredentialsFactory'; export * from './skillValidation'; +export * from './tokenValidationParameters'; export * from './userTokenClient'; export { MsalAppCredentials } from './msalAppCredentials'; diff --git a/libraries/botframework-connector/src/auth/jwtTokenValidation.ts b/libraries/botframework-connector/src/auth/jwtTokenValidation.ts index 8cf1858e35..be593f6b6f 100644 --- a/libraries/botframework-connector/src/auth/jwtTokenValidation.ts +++ b/libraries/botframework-connector/src/auth/jwtTokenValidation.ts @@ -19,6 +19,7 @@ import { EnterpriseChannelValidation } from './enterpriseChannelValidation'; import { GovernmentChannelValidation } from './governmentChannelValidation'; import { GovernmentConstants } from './governmentConstants'; import { SkillValidation } from './skillValidation'; +import { AseChannelValidation } from './aseChannelValidation'; /** * @deprecated Use `ConfigurationBotFrameworkAuthentication` instead to perform JWT token validation. @@ -128,6 +129,10 @@ export namespace JwtTokenValidation { authConfig: AuthenticationConfiguration, serviceUrl: string ): Promise { + if (AseChannelValidation.isTokenFromAseChannel(channelId)) { + return AseChannelValidation.authenticateAseChannelToken(authHeader); + } + if (SkillValidation.isSkillToken(authHeader)) { return await SkillValidation.authenticateChannelToken( authHeader, @@ -138,9 +143,7 @@ export namespace JwtTokenValidation { ); } - const usingEmulator = EmulatorValidation.isTokenFromEmulator(authHeader); - - if (usingEmulator) { + if (EmulatorValidation.isTokenFromEmulator(authHeader)) { return await EmulatorValidation.authenticateEmulatorToken( authHeader, credentials, diff --git a/libraries/botframework-connector/src/auth/microsoftGovernmentAppCredentials.ts b/libraries/botframework-connector/src/auth/microsoftGovernmentAppCredentials.ts new file mode 100644 index 0000000000..4eb51a887a --- /dev/null +++ b/libraries/botframework-connector/src/auth/microsoftGovernmentAppCredentials.ts @@ -0,0 +1,39 @@ +/** + * @module botframework-connector + */ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import { GovernmentConstants } from './governmentConstants'; +import { MicrosoftAppCredentials } from './microsoftAppCredentials'; + +/** + * MicrosoftGovermentAppCredentials auth implementation + */ +export class MicrosoftGovernmentAppCredentials extends MicrosoftAppCredentials { + /** + * Initializes a new instance of the [MicrosoftGovernmentAppCredentials](xref:botframework-connector.MicrosoftGovernmentAppCredentials) class. + * + * @param {string} appId The Microsoft app ID. + * @param {string} appPassword The Microsoft app password. + * @param {string} channelAuthTenant Optional. The oauth token tenant. + * @param {string} oAuthScope Optional. The scope for the token. + */ + constructor(appId: string, public appPassword: string, channelAuthTenant?: string, oAuthScope?: string) { + super(appId, appPassword, channelAuthTenant, oAuthScope); + } + + protected GetToChannelFromBotOAuthScope(): string { + return GovernmentConstants.ToChannelFromBotOAuthScope; + } + + protected GetToChannelFromBotLoginUrlPrefix(): string { + return GovernmentConstants.ToChannelFromBotLoginUrlPrefix; + } + + protected GetDefaultChannelAuthTenant(): string { + return GovernmentConstants.DefaultChannelAuthTenant; + } +} diff --git a/libraries/botframework-connector/src/auth/msalServiceClientCredentialsFactory.ts b/libraries/botframework-connector/src/auth/msalServiceClientCredentialsFactory.ts index e807aa513e..997915d4c7 100644 --- a/libraries/botframework-connector/src/auth/msalServiceClientCredentialsFactory.ts +++ b/libraries/botframework-connector/src/auth/msalServiceClientCredentialsFactory.ts @@ -70,11 +70,11 @@ export class MsalServiceClientCredentialsFactory implements ServiceClientCredent ); } - if (normalizedEndpoint === GovernmentConstants.ToChannelFromBotLoginUrl.toLowerCase()) { + if (normalizedEndpoint.startsWith(GovernmentConstants.ToChannelFromBotLoginUrlPrefix)) { return new MsalAppCredentials( this.clientApplication, appId, - GovernmentConstants.ToChannelFromBotLoginUrl, + undefined, audience || GovernmentConstants.ToChannelFromBotOAuthScope ); } diff --git a/libraries/botframework-connector/src/auth/parameterizedBotFrameworkAuthentication.ts b/libraries/botframework-connector/src/auth/parameterizedBotFrameworkAuthentication.ts index d6e8f20dd9..21d14f02ce 100644 --- a/libraries/botframework-connector/src/auth/parameterizedBotFrameworkAuthentication.ts +++ b/libraries/botframework-connector/src/auth/parameterizedBotFrameworkAuthentication.ts @@ -22,6 +22,7 @@ import { ToBotFromBotOrEmulatorTokenValidationParameters } from './tokenValidati import { UserTokenClientImpl } from './userTokenClientImpl'; import type { UserTokenClient } from './userTokenClient'; import { VerifyOptions } from 'jsonwebtoken'; +import { AseChannelValidation } from './aseChannelValidation'; function getAppId(claimsIdentity: ClaimsIdentity): string | undefined { // For requests from channel App Id is in Audience claim of JWT token. For emulator it is in AppId claim. For @@ -270,6 +271,10 @@ export class ParameterizedBotFrameworkAuthentication extends BotFrameworkAuthent channelId: string, serviceUrl: string ): Promise { + if (AseChannelValidation.isTokenFromAseChannel(channelId)) { + return AseChannelValidation.authenticateAseChannelToken(authHeader); + } + if (SkillValidation.isSkillToken(authHeader)) { return this.SkillValidation_authenticateChannelToken(authHeader, channelId); } diff --git a/libraries/botframework-connector/src/auth/passwordServiceClientCredentialFactory.ts b/libraries/botframework-connector/src/auth/passwordServiceClientCredentialFactory.ts index ec01185056..11e60bce7a 100644 --- a/libraries/botframework-connector/src/auth/passwordServiceClientCredentialFactory.ts +++ b/libraries/botframework-connector/src/auth/passwordServiceClientCredentialFactory.ts @@ -8,6 +8,7 @@ import type { ServiceClientCredentials } from '@azure/core-http'; import { AuthenticationConstants } from './authenticationConstants'; import { GovernmentConstants } from './governmentConstants'; import { MicrosoftAppCredentials } from './microsoftAppCredentials'; +import { MicrosoftGovernmentAppCredentials } from './microsoftGovernmentAppCredentials'; import { ServiceClientCredentialsFactory } from './serviceClientCredentialsFactory'; import { stringExt } from 'botbuilder-stdlib'; @@ -111,9 +112,8 @@ export class PasswordServiceClientCredentialFactory implements ServiceClientCred if (normalizedEndpoint?.startsWith(AuthenticationConstants.ToChannelFromBotLoginUrlPrefix)) { credentials = new MicrosoftAppCredentials(appId, this.password, this.tenantId, audience); - } else if (normalizedEndpoint === GovernmentConstants.ToChannelFromBotLoginUrl.toLowerCase()) { - credentials = new MicrosoftAppCredentials(appId, this.password, this.tenantId, audience); - credentials.oAuthEndpoint = loginEndpoint; + } else if (normalizedEndpoint?.startsWith(GovernmentConstants.ToChannelFromBotLoginUrlPrefix)) { + credentials = new MicrosoftGovernmentAppCredentials(appId, this.password, this.tenantId, audience); } else { credentials = new PrivateCloudAppCredentials( appId, diff --git a/libraries/botframework-connector/src/auth/tokenValidationParameters.ts b/libraries/botframework-connector/src/auth/tokenValidationParameters.ts index f0bd53b89f..6bc439402c 100644 --- a/libraries/botframework-connector/src/auth/tokenValidationParameters.ts +++ b/libraries/botframework-connector/src/auth/tokenValidationParameters.ts @@ -23,3 +23,14 @@ export const ToBotFromBotOrEmulatorTokenValidationParameters: VerifyOptions = { clockTolerance: 5 * 60, ignoreExpiration: false, }; + +// Internal +/** + * Default options for validating incoming tokens from the Bot Ase Channel. + */ +export const BetweenBotAndAseChannelTokenValidationParameters: VerifyOptions = { + issuer: [], + audience: undefined, // Audience validation takes place manually in code. + clockTolerance: 5 * 60, + ignoreExpiration: false, +}; diff --git a/libraries/botframework-connector/tests/auth/aseChannelValidation.test.js b/libraries/botframework-connector/tests/auth/aseChannelValidation.test.js new file mode 100644 index 0000000000..5d2c167d1c --- /dev/null +++ b/libraries/botframework-connector/tests/auth/aseChannelValidation.test.js @@ -0,0 +1,67 @@ +const { + AseChannelValidation, + GovernmentConstants, + AuthenticationConstants, + BetweenBotAndAseChannelTokenValidationParameters, +} = require('../..'); +const assert = require('assert'); + +describe('AseChannelTestSuite', function () { + describe('AseChannelTestCase', function () { + it('ValidationMetadataUrlTest_AseChannel_USGov', function () { + const config = { + ChannelService: GovernmentConstants.ChannelService, + }; + AseChannelValidation.init(config); + assert.strictEqual( + AseChannelValidation.MetadataUrl, + GovernmentConstants.ToBotFromEmulatorOpenIdMetadataUrl + ); + }); + + it('ValidationMetadataUrlTest_AseChannel_Public', function () { + const config = {}; + AseChannelValidation.init(config); + assert.strictEqual( + AseChannelValidation.MetadataUrl, + AuthenticationConstants.ToBotFromEmulatorOpenIdMetadataUrl + ); + }); + + it('ValidationIssueUrlTest_AseChannel', function () { + const config = { + MicrosoftAppTenantId: 'testTenantId', + }; + AseChannelValidation.init(config); + const tenantIds = [ + 'testTenantId', + 'f8cdef31-a31e-4b4a-93e4-5f571e91255a', // US Gov MicrosoftServices.onmicrosoft.us + 'd6d49420-f39b-4df7-a1dc-d59a935871db', // Public botframework.com + ]; + tenantIds.forEach(function (tmpId) { + assert.strictEqual( + true, + BetweenBotAndAseChannelTokenValidationParameters.issuer.includes( + `https://sts.windows.net/${tmpId}/` + ) + ); + assert.strictEqual( + true, + BetweenBotAndAseChannelTokenValidationParameters.issuer.includes( + `https://login.microsoftonline.com/${tmpId}/v2.0` + ) + ); + assert.strictEqual( + true, + BetweenBotAndAseChannelTokenValidationParameters.issuer.includes( + `https://login.microsoftonline.us/${tmpId}/v2.0` + ) + ); + }); + }); + + it('ValidationChannelIdTest_AseChannel', function () { + assert.strictEqual(true, AseChannelValidation.isTokenFromAseChannel('AseChannel')); + }); + }); +}); diff --git a/libraries/botframework-connector/tests/auth/microsoftAppCredentials.test.js b/libraries/botframework-connector/tests/auth/microsoftAppCredentials.test.js new file mode 100644 index 0000000000..83ca0f8059 --- /dev/null +++ b/libraries/botframework-connector/tests/auth/microsoftAppCredentials.test.js @@ -0,0 +1,23 @@ +const { MicrosoftAppCredentials, AuthenticationConstants } = require('../..'); +const assert = require('assert'); + +describe('MicrosoftAppCredentialsTestSuite', function () { + describe('MicrosoftAppCredentialsTestCase', function () { + it('AssertOAuthEndpointAndOAuthScope', function () { + const credentials1 = new MicrosoftAppCredentials('appId', 'password', 'tenantId', 'audience'); + assert.strictEqual( + AuthenticationConstants.ToChannelFromBotLoginUrlPrefix + 'tenantId', + credentials1.oAuthEndpoint + ); + assert.strictEqual('audience', credentials1.oAuthScope); + + const credentials2 = new MicrosoftAppCredentials('appId', 'password'); + assert.strictEqual( + AuthenticationConstants.ToChannelFromBotLoginUrlPrefix + + AuthenticationConstants.DefaultChannelAuthTenant, + credentials2.oAuthEndpoint + ); + assert.strictEqual(AuthenticationConstants.ToChannelFromBotOAuthScope, credentials2.oAuthScope); + }); + }); +}); diff --git a/libraries/botframework-connector/tests/auth/microsoftGovernmentAppCredentials.test.js b/libraries/botframework-connector/tests/auth/microsoftGovernmentAppCredentials.test.js new file mode 100644 index 0000000000..01e9b7b166 --- /dev/null +++ b/libraries/botframework-connector/tests/auth/microsoftGovernmentAppCredentials.test.js @@ -0,0 +1,22 @@ +const { MicrosoftGovernmentAppCredentials, GovernmentConstants } = require('../..'); +const assert = require('assert'); + +describe('MicrosoftGovernmentAppCredentialsTestSuite', function () { + describe('MicrosoftGovernmentAppCredentialsTestCase', function () { + it('AssertOAuthEndpointAndOAuthScope', function () { + const credentials1 = new MicrosoftGovernmentAppCredentials('appId', 'password', 'tenantId', 'audience'); + assert.strictEqual( + GovernmentConstants.ToChannelFromBotLoginUrlPrefix + 'tenantId', + credentials1.oAuthEndpoint + ); + assert.strictEqual('audience', credentials1.oAuthScope); + + const credentials2 = new MicrosoftGovernmentAppCredentials('appId', 'password'); + assert.strictEqual( + GovernmentConstants.ToChannelFromBotLoginUrlPrefix + GovernmentConstants.DefaultChannelAuthTenant, + credentials2.oAuthEndpoint + ); + assert.strictEqual(GovernmentConstants.ToChannelFromBotOAuthScope, credentials2.oAuthScope); + }); + }); +});