-
Notifications
You must be signed in to change notification settings - Fork 277
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add additional BotFrameworkAuthentication classes for CloudAdap…
…ter (#3698) * add additional BotFrameworkAuthentication classes for CloudAdapter * Add Alexa to Channels enum for #3603 * fix export, USER_AGENT and style issue * make path in Configuration.get optional, apply other PR feedback * address additional PR feedback, minor cleanup * update adaptive-runtime.Configuration to reflect contract * cleanup typing, add unit tests, refactor existing code * fix linting error in types.ts
- Loading branch information
Showing
30 changed files
with
1,518 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
221 changes: 221 additions & 0 deletions
221
libraries/botbuilder-core/src/configurationBotFrameworkAuthentication.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import { Activity } from 'botframework-schema'; | ||
import { | ||
AuthenticateRequestResult, | ||
AuthenticationConfiguration, | ||
AuthenticationConstants, | ||
BotFrameworkAuthentication, | ||
BotFrameworkAuthenticationFactory, | ||
BotFrameworkClient, | ||
ClaimsIdentity, | ||
ConnectorClientOptions, | ||
ConnectorFactory, | ||
ServiceClientCredentialsFactory, | ||
UserTokenClient, | ||
} from 'botframework-connector'; | ||
import { Configuration } from 'botbuilder-dialogs-adaptive-runtime-core'; | ||
import { | ||
ConfigurationServiceClientCredentialFactory, | ||
ConfigurationServiceClientCredentialFactoryOptions, | ||
} from './configurationServiceClientCredentialFactory'; | ||
import * as t from 'runtypes'; | ||
import { ValidationError } from 'runtypes'; | ||
|
||
const TypedOptions = t.Record({ | ||
/** | ||
* (Optional) The OAuth URL used to get a token from OAuthApiClient. The "OAuthUrl" member takes precedence over this value. | ||
*/ | ||
[AuthenticationConstants.OAuthUrlKey]: t.String.nullable().optional(), | ||
|
||
/** | ||
* (Optional) The OpenID metadata document used for authenticating tokens coming from the channel. The "ToBotFromChannelOpenIdMetadataUrl" member takes precedence over this value. | ||
*/ | ||
[AuthenticationConstants.BotOpenIdMetadataKey]: t.String.nullable().optional(), | ||
|
||
/** | ||
* A string used to indicate if which cloud the bot is operating in (e.g. Public Azure or US Government). | ||
* | ||
* @remarks | ||
* A `null` or `''` value indicates Public Azure, whereas [GovernmentConstants.ChannelService](xref:botframework-connector.GovernmentConstants.ChannelService) indicates the bot is operating in the US Government cloud. | ||
* | ||
* Other values result in a custom authentication configuration derived from the values passed in on the [ConfigurationBotFrameworkAuthenticationOptions](xef:botbuilder-core.ConfigurationBotFrameworkAuthenticationOptions) instance. | ||
*/ | ||
[AuthenticationConstants.ChannelService]: t.String.nullable().optional(), | ||
|
||
/** | ||
* Flag indicating whether or not to validate the address. | ||
*/ | ||
ValidateAuthority: t.String.Or(t.Boolean), | ||
|
||
/** | ||
* The Login URL used to specify the tenant from which the bot should obtain access tokens from. | ||
*/ | ||
ToChannelFromBotLoginUrl: t.String, | ||
|
||
/** | ||
* The Oauth scope to request. | ||
* | ||
* @remarks | ||
* This value is used when fetching a token to indicate the ultimate recipient or `audience` of an activity sent using these credentials. | ||
*/ | ||
ToChannelFromBotOAuthScope: t.String, | ||
|
||
/** | ||
* The Token issuer for signed requests to the channel. | ||
*/ | ||
ToBotFromChannelTokenIssuer: t.String, | ||
|
||
/** | ||
* The OAuth URL used to get a token from OAuthApiClient. | ||
*/ | ||
OAuthUrl: t.String, | ||
|
||
/** | ||
* The OpenID metadata document used for authenticating tokens coming from the channel. | ||
*/ | ||
ToBotFromChannelOpenIdMetadataUrl: t.String, | ||
|
||
/** | ||
* The The OpenID metadata document used for authenticating tokens coming from the Emulator. | ||
*/ | ||
ToBotFromEmulatorOpenIdMetadataUrl: t.String, | ||
|
||
/** | ||
* A value for the CallerId. | ||
*/ | ||
CallerId: t.String, | ||
}); | ||
|
||
/** | ||
* Contains settings used to configure a [ConfigurationBotFrameworkAuthentication](xref:botbuilder-core.ConfigurationBotFrameworkAuthentication) instance. | ||
*/ | ||
export type ConfigurationBotFrameworkAuthenticationOptions = t.Static<typeof TypedOptions>; | ||
|
||
/** | ||
* Creates a [BotFrameworkAuthentication](xref:botframework-connector.BotFrameworkAuthentication) instance from an object with the authentication values or a [Configuration](xref:botbuilder-dialogs-adaptive-runtime-core.Configuration) instance. | ||
*/ | ||
export class ConfigurationBotFrameworkAuthentication extends BotFrameworkAuthentication { | ||
private readonly inner: BotFrameworkAuthentication; | ||
|
||
/** | ||
* Initializes a new instance of the [ConfigurationBotFrameworkAuthentication](xref:botbuilder-core.ConfigurationBotFrameworkAuthentication) class. | ||
* | ||
* @param botFrameworkAuthConfig A [ConfigurationBotFrameworkAuthenticationOptions](xref:botbuilder-core.ConfigurationBotFrameworkAuthenticationOptions) object. | ||
* @param credentialsFactory A [ServiceClientCredentialsFactory](xref:botframework-connector.ServiceClientCredentialsFactory) instance. | ||
* @param authConfiguration A [Configuration](xref:botframework-connector.AuthenticationConfiguration) object. | ||
* @param botFrameworkClientFetch A custom Fetch implementation to be used in the [BotFrameworkClient](xref:botframework-connector.BotFrameworkClient). | ||
* @param connectorClientOptions A [ConnectorClientOptions](xref:botframework-connector.ConnectorClientOptions) object. | ||
*/ | ||
constructor( | ||
botFrameworkAuthConfig: Partial<ConfigurationBotFrameworkAuthenticationOptions>, | ||
credentialsFactory?: ServiceClientCredentialsFactory, | ||
authConfiguration?: AuthenticationConfiguration, | ||
botFrameworkClientFetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>, | ||
connectorClientOptions: ConnectorClientOptions = {} | ||
) { | ||
super(); | ||
try { | ||
TypedOptions.check(botFrameworkAuthConfig); | ||
} catch (err) { | ||
// Throw a new error with the validation details prominently featured. | ||
if (err instanceof ValidationError && err.details) { | ||
throw new Error(JSON.stringify(err.details, undefined, 2)); | ||
} | ||
throw err; | ||
} | ||
const { | ||
ChannelService, | ||
ToChannelFromBotLoginUrl, | ||
ToChannelFromBotOAuthScope, | ||
ToBotFromChannelTokenIssuer, | ||
ToBotFromEmulatorOpenIdMetadataUrl, | ||
CallerId, | ||
} = botFrameworkAuthConfig; | ||
|
||
const ToBotFromChannelOpenIdMetadataUrl = | ||
botFrameworkAuthConfig.ToBotFromChannelOpenIdMetadataUrl ?? | ||
botFrameworkAuthConfig[AuthenticationConstants.BotOpenIdMetadataKey]; | ||
const OAuthUrl = botFrameworkAuthConfig.OAuthUrl ?? botFrameworkAuthConfig[AuthenticationConstants.OAuthUrlKey]; | ||
let ValidateAuthority = true; | ||
try { | ||
ValidateAuthority = Boolean(JSON.parse(botFrameworkAuthConfig.ValidateAuthority as string)); | ||
} catch (_err) { | ||
// no-op | ||
} | ||
|
||
this.inner = BotFrameworkAuthenticationFactory.create( | ||
ChannelService, | ||
ValidateAuthority, | ||
ToChannelFromBotLoginUrl, | ||
ToChannelFromBotOAuthScope, | ||
ToBotFromChannelTokenIssuer, | ||
OAuthUrl, | ||
ToBotFromChannelOpenIdMetadataUrl, | ||
ToBotFromEmulatorOpenIdMetadataUrl, | ||
CallerId, | ||
credentialsFactory ?? | ||
new ConfigurationServiceClientCredentialFactory( | ||
botFrameworkAuthConfig as ConfigurationServiceClientCredentialFactoryOptions | ||
), | ||
authConfiguration ?? ({} as AuthenticationConfiguration), | ||
botFrameworkClientFetch, | ||
connectorClientOptions | ||
); | ||
} | ||
|
||
authenticateChannelRequest(authHeader: string): Promise<ClaimsIdentity> { | ||
return this.inner.authenticateChannelRequest(authHeader); | ||
} | ||
|
||
authenticateRequest(activity: Activity, authHeader: string): Promise<AuthenticateRequestResult> { | ||
return this.inner.authenticateRequest(activity, authHeader); | ||
} | ||
|
||
authenticateStreamingRequest(authHeader: string, channelIdHeader: string): Promise<AuthenticateRequestResult> { | ||
return this.inner.authenticateStreamingRequest(authHeader, channelIdHeader); | ||
} | ||
|
||
createBotFrameworkClient(): BotFrameworkClient { | ||
return this.inner.createBotFrameworkClient(); | ||
} | ||
|
||
createConnectorFactory(claimsIdentity: ClaimsIdentity): ConnectorFactory { | ||
return this.inner.createConnectorFactory(claimsIdentity); | ||
} | ||
|
||
createUserTokenClient(claimsIdentity: ClaimsIdentity): Promise<UserTokenClient> { | ||
return this.inner.createUserTokenClient(claimsIdentity); | ||
} | ||
} | ||
|
||
/** | ||
* Creates a new instance of the [ConfigurationBotFrameworkAuthentication](xref:botbuilder-core.ConfigurationBotFrameworkAuthentication) class. | ||
* | ||
* @remarks | ||
* The [Configuration](xref:botbuilder-dialogs-adaptive-runtime-core.Configuration) instance provided to the constructor should | ||
* have the desired authentication values available at the root, using the properties of [ConfigurationBotFrameworkAuthenticationOptions](xref:botbuilder-core.ConfigurationBotFrameworkAuthenticationOptions) as its keys. | ||
* @param configuration A [Configuration](xref:botbuilder-dialogs-adaptive-runtime-core.Configuration) instance. | ||
* @param credentialsFactory A [ServiceClientCredentialsFactory](xref:botframework-connector.ServiceClientCredentialsFactory) instance. | ||
* @param authConfiguration A [Configuration](xref:botframework-connector.AuthenticationConfiguration) object. | ||
* @param botFrameworkClientFetch A custom Fetch implementation to be used in the [BotFrameworkClient](xref:botframework-connector.BotFrameworkClient). | ||
* @param connectorClientOptions A [ConnectorClientOptions](xref:botframework-connector.ConnectorClientOptions) object. | ||
* @returns A [ConfigurationBotFrameworkAuthentication](xref:botbuilder-core.ConfigurationBotFrameworkAuthentication) instance. | ||
*/ | ||
export function createBotFrameworkAuthenticationFromConfiguration( | ||
configuration: Configuration, | ||
credentialsFactory: ServiceClientCredentialsFactory = null, | ||
authConfiguration: AuthenticationConfiguration = null, | ||
botFrameworkClientFetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>, | ||
connectorClientOptions: ConnectorClientOptions = {} | ||
): BotFrameworkAuthentication { | ||
const botFrameworkAuthConfig = configuration?.get<ConfigurationBotFrameworkAuthenticationOptions>(); | ||
return new ConfigurationBotFrameworkAuthentication( | ||
botFrameworkAuthConfig, | ||
credentialsFactory, | ||
authConfiguration, | ||
botFrameworkClientFetch, | ||
connectorClientOptions | ||
); | ||
} |
70 changes: 70 additions & 0 deletions
70
libraries/botbuilder-core/src/configurationServiceClientCredentialFactory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import { Configuration } from 'botbuilder-dialogs-adaptive-runtime-core'; | ||
import { PasswordServiceClientCredentialFactory } from 'botframework-connector'; | ||
import * as t from 'runtypes'; | ||
import { ValidationError } from 'runtypes'; | ||
|
||
const TypedConfig = t.Record({ | ||
/** | ||
* The ID assigned to your bot in the [Bot Framework Portal](https://dev.botframework.com/). | ||
*/ | ||
MicrosoftAppId: t.String.nullable().optional(), | ||
|
||
/** | ||
* The password assigned to your bot in the [Bot Framework Portal](https://dev.botframework.com/). | ||
*/ | ||
MicrosoftAppPassword: t.String.nullable().optional(), | ||
}); | ||
|
||
/** | ||
* Contains settings used to configure a [ConfigurationServiceClientCredentialFactory](xref:botbuilder-core.ConfigurationServiceClientCredentialFactory) instance. | ||
*/ | ||
export type ConfigurationServiceClientCredentialFactoryOptions = t.Static<typeof TypedConfig>; | ||
|
||
const isValidationErrorWithDetails = (val: unknown): val is ValidationError => { | ||
return !!(val as ValidationError)?.details; | ||
}; | ||
|
||
/** | ||
* ServiceClientCredentialsFactory that uses a [ConfigurationServiceClientCredentialFactoryOptions](xref:botbuilder-core.ConfigurationServiceClientCredentialFactoryOptions) or a [Configuration](xref:botbuilder-dialogs-adaptive-runtime-core.Configuration) instance to build ServiceClientCredentials with an AppId and App Password. | ||
*/ | ||
export class ConfigurationServiceClientCredentialFactory extends PasswordServiceClientCredentialFactory { | ||
/** | ||
* Initializes a new instance of the [ConfigurationServiceClientCredentialFactory](xref:botbuilder-core.ConfigurationServiceClientCredentialFactory) class. | ||
* | ||
* @param factoryOptions A [ConfigurationServiceClientCredentialFactoryOptions](xref:botbuilder-core.ConfigurationServiceClientCredentialFactoryOptions) object. | ||
*/ | ||
constructor(factoryOptions: ConfigurationServiceClientCredentialFactoryOptions) { | ||
super(null, null); | ||
try { | ||
const { MicrosoftAppId, MicrosoftAppPassword } = TypedConfig.check(factoryOptions); | ||
this.appId = MicrosoftAppId ?? this.appId; | ||
this.password = MicrosoftAppPassword ?? this.password; | ||
} catch (err) { | ||
if (isValidationErrorWithDetails(err)) { | ||
const e = new Error(JSON.stringify(err.details, undefined, 2)); | ||
e.stack = err.stack; | ||
throw e; | ||
} | ||
throw err; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Creates a new instance of the [ConfigurationServiceClientCredentialFactory](xref:botbuilder-core.ConfigurationServiceClientCredentialFactory) class. | ||
* | ||
* @remarks | ||
* The [Configuration](xref:botbuilder-dialogs-adaptive-runtime-core.Configuration) instance provided to the constructor should | ||
* have the desired authentication values available at the root, using the properties of [ConfigurationServiceClientCredentialFactoryOptions](xref:botbuilder-core.ConfigurationServiceClientCredentialFactoryOptions) as its keys. | ||
* @param configuration A [Configuration](xref:botbuilder-dialogs-adaptive-runtime-core.Configuration) instance. | ||
* @returns A [ConfigurationServiceClientCredentialFactory](xref:botbuilder-core.ConfigurationServiceClientCredentialFactory) instance. | ||
*/ | ||
export function createServiceClientCredentialFactoryFromConfiguration( | ||
configuration: Configuration | ||
): ConfigurationServiceClientCredentialFactory { | ||
const factoryOptions = configuration.get<ConfigurationServiceClientCredentialFactoryOptions>(); | ||
return new ConfigurationServiceClientCredentialFactory(factoryOptions); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
libraries/botbuilder-core/tests/configurationBotFrameworkAuthentication.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
const assert = require('assert'); | ||
const { AuthenticationConstants } = require('botframework-connector'); | ||
const { | ||
CallerIdConstants, | ||
ConfigurationBotFrameworkAuthentication, | ||
createBotFrameworkAuthenticationFromConfiguration, | ||
} = require('../'); | ||
|
||
describe('ConfigurationBotFrameworkAuthentication', function () { | ||
class TestConfiguration { | ||
static DefaultConfig = { | ||
// [AuthenticationConstants.ChannelService]: undefined, | ||
ValidateAuthority: true, | ||
ToChannelFromBotLoginUrl: AuthenticationConstants.ToChannelFromBotLoginUrl, | ||
ToChannelFromBotOAuthScope: AuthenticationConstants.ToChannelFromBotOAuthScope, | ||
ToBotFromChannelTokenIssuer: AuthenticationConstants.ToBotFromChannelTokenIssuer, | ||
ToBotFromEmulatorOpenIdMetadataUrl: AuthenticationConstants.ToBotFromEmulatorOpenIdMetadataUrl, | ||
CallerId: CallerIdConstants.PublicAzureChannel, | ||
ToBotFromChannelOpenIdMetadataUrl: AuthenticationConstants.ToBotFromChannelOpenIdMetadataUrl, | ||
OAuthUrl: AuthenticationConstants.OAuthUrl, | ||
// [AuthenticationConstants.OAuthUrlKey]: 'test', | ||
[AuthenticationConstants.BotOpenIdMetadataKey]: null, | ||
}; | ||
|
||
constructor(config = {}) { | ||
this.configuration = Object.assign({}, TestConfiguration.DefaultConfig, config); | ||
} | ||
|
||
get(_path) { | ||
return this.configuration; | ||
} | ||
|
||
set(_path, _val) {} | ||
} | ||
|
||
it('constructor should work', function () { | ||
const bfAuth = new ConfigurationBotFrameworkAuthentication(TestConfiguration.DefaultConfig); | ||
assert(bfAuth.inner); | ||
}); | ||
|
||
it('createBotFrameworkAuthenticationFromConfiguration should work', function () { | ||
const config = new TestConfiguration(); | ||
const bfAuth = createBotFrameworkAuthenticationFromConfiguration(config); | ||
assert(bfAuth.inner); | ||
}); | ||
}); |
Oops, something went wrong.