-
Notifications
You must be signed in to change notification settings - Fork 281
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Add FIC support * Fix lint * Add appId validation
- Loading branch information
1 parent
c88cf3b
commit 021429c
Showing
3 changed files
with
160 additions
and
1 deletion.
There are no files selected for viewing
91 changes: 91 additions & 0 deletions
91
libraries/botframework-connector/src/auth/federatedAppCredentials.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,91 @@ | ||
/** | ||
* @module botframework-connector | ||
*/ | ||
/** | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. | ||
*/ | ||
|
||
import { ConfidentialClientApplication, ManagedIdentityApplication } from '@azure/msal-node'; | ||
import { ok } from 'assert'; | ||
import { AppCredentials } from './appCredentials'; | ||
import { AuthenticatorResult } from './authenticatorResult'; | ||
import { MsalAppCredentials } from './msalAppCredentials'; | ||
|
||
/** | ||
* Federated Credentials auth implementation. | ||
*/ | ||
export class FederatedAppCredentials extends AppCredentials { | ||
private credentials: MsalAppCredentials; | ||
private managedIdentityClientAssertion: ManagedIdentityApplication; | ||
private clientAudience: string; | ||
|
||
/** | ||
* Initializes a new instance of the [FederatedAppCredentials](xref:botframework-connector.FederatedAppCredentials) class. | ||
* | ||
* @param {string} appId App ID for the Application. | ||
* @param {string} clientId Client ID for the managed identity assigned to the bot. | ||
* @param {string} channelAuthTenant Tenant ID of the Azure AD tenant where the bot is created. | ||
* * **Required** for SingleTenant app types. | ||
* * **Optional** for MultiTenant app types. **Note**: '_botframework.com_' is the default tenant when no value is provided. | ||
* | ||
* More information: https://learn.microsoft.com/en-us/security/zero-trust/develop/identity-supported-account-types. | ||
* @param {string} oAuthScope **Optional**. The scope for the token. | ||
* @param {string} clientAudience **Optional**. The Audience used in the Client's Federated Credential. **Default** (_api://AzureADTokenExchange_). | ||
*/ | ||
constructor( | ||
appId: string, | ||
clientId: string, | ||
channelAuthTenant?: string, | ||
oAuthScope?: string, | ||
clientAudience?: string | ||
) { | ||
super(appId, channelAuthTenant, oAuthScope); | ||
|
||
ok(appId?.trim(), 'FederatedAppCredentials.constructor(): missing appId.'); | ||
|
||
this.clientAudience = clientAudience ?? 'api://AzureADTokenExchange'; | ||
this.managedIdentityClientAssertion = new ManagedIdentityApplication({ | ||
managedIdentityIdParams: { userAssignedClientId: clientId }, | ||
}); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
async getToken(forceRefresh = false): Promise<string> { | ||
this.credentials ??= new MsalAppCredentials( | ||
this.createClientApplication(await this.fetchExternalToken(forceRefresh)), | ||
this.oAuthEndpoint, | ||
this.oAuthEndpoint, | ||
this.oAuthScope | ||
); | ||
return this.credentials.getToken(forceRefresh); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
protected refreshToken(): Promise<AuthenticatorResult> { | ||
// This will never be executed because we are using MsalAppCredentials.getToken underneath. | ||
throw new Error('Method not implemented.'); | ||
} | ||
|
||
private createClientApplication(clientAssertion: string) { | ||
return new ConfidentialClientApplication({ | ||
auth: { | ||
clientId: this.appId, | ||
authority: this.oAuthEndpoint, | ||
clientAssertion, | ||
}, | ||
}); | ||
} | ||
|
||
private async fetchExternalToken(forceRefresh = false): Promise<string> { | ||
const response = await this.managedIdentityClientAssertion.acquireToken({ | ||
resource: this.clientAudience, | ||
forceRefresh, | ||
}); | ||
return response.accessToken; | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
libraries/botframework-connector/src/auth/federatedServiceClientCredentialsFactory.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,66 @@ | ||
/** | ||
* @module botframework-connector | ||
*/ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import { ok } from 'assert'; | ||
import type { ServiceClientCredentials } from '@azure/core-http'; | ||
import { ServiceClientCredentialsFactory } from './serviceClientCredentialsFactory'; | ||
import { FederatedAppCredentials } from './federatedAppCredentials'; | ||
|
||
/** | ||
* A Federated Credentials implementation of the [ServiceClientCredentialsFactory](xref:botframework-connector.ServiceClientCredentialsFactory) interface. | ||
*/ | ||
export class FederatedServiceClientCredentialsFactory extends ServiceClientCredentialsFactory { | ||
/** | ||
* Initializes a new instance of the [FederatedServiceClientCredentialsFactory](xref:botframework-connector.FederatedServiceClientCredentialsFactory) class. | ||
* | ||
* @param {string} appId App ID for the Application. | ||
* @param {string} clientId Client ID for the managed identity assigned to the bot. | ||
* @param {string} tenantId Tenant ID of the Azure AD tenant where the bot is created. | ||
* * **Required** for SingleTenant app types. | ||
* * **Optional** for MultiTenant app types. **Note**: '_botframework.com_' is the default tenant when no value is provided. | ||
* | ||
* More information: https://learn.microsoft.com/en-us/security/zero-trust/develop/identity-supported-account-types. | ||
* @param {string} clientAudience **Optional**. The Audience used in the Client's Federated Credential. **Default** (_api://AzureADTokenExchange_). | ||
*/ | ||
constructor( | ||
private appId: string, | ||
private clientId: string, | ||
private tenantId?: string, | ||
private clientAudience?: string | ||
) { | ||
super(); | ||
|
||
ok(appId?.trim(), 'FederatedServiceClientCredentialsFactory.constructor(): missing appId.'); | ||
ok(clientId?.trim(), 'FederatedServiceClientCredentialsFactory.constructor(): missing clientId.'); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
async isValidAppId(appId = ''): Promise<boolean> { | ||
return appId === this.appId; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
async isAuthenticationDisabled(): Promise<boolean> { | ||
// Auth is always enabled for FIC. | ||
return; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
async createCredentials(appId: string, audience: string): Promise<ServiceClientCredentials> { | ||
ok( | ||
await this.isValidAppId(appId), | ||
'FederatedServiceClientCredentialsFactory.createCredentials(): Invalid App ID.' | ||
); | ||
|
||
return new FederatedAppCredentials(this.appId, this.clientId, this.tenantId, audience, this.clientAudience); | ||
} | ||
} |
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