forked from microsoft/botbuilder-js
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: [microsoft#2782] Migrate to MSAL from adal-node - Add MSAL suppo…
…rt (microsoft#4543) * Add MSAL support * Remove support old TS versions 3.5, 3.6, 3.7
- Loading branch information
1 parent
123cc88
commit fff65b4
Showing
8 changed files
with
256 additions
and
5 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
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
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
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
136 changes: 136 additions & 0 deletions
136
libraries/botframework-connector/src/auth/msalAppCredentials.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,136 @@ | ||
/** | ||
* @module botframework-connector | ||
*/ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import { AppCredentials } from './appCredentials'; | ||
import { ConfidentialClientApplication, NodeAuthOptions } from '@azure/msal-node'; | ||
import { TokenResponse } from 'adal-node'; | ||
|
||
export interface Certificate { | ||
thumbprint: string; | ||
privateKey: string; | ||
} | ||
|
||
/** | ||
* An implementation of AppCredentials that uses @azure/msal-node to fetch tokens. | ||
*/ | ||
export class MsalAppCredentials extends AppCredentials { | ||
/** | ||
* A reference used for Empty auth scenarios | ||
*/ | ||
static Empty = new MsalAppCredentials(); | ||
|
||
private readonly clientApplication?: ConfidentialClientApplication; | ||
|
||
/** | ||
* Create an MsalAppCredentials instance using a confidential client application. | ||
* | ||
* @param clientApplication An @azure/msal-node ConfidentialClientApplication instance. | ||
* @param appId The application ID. | ||
* @param authority The authority to use for fetching tokens | ||
* @param scope The oauth scope to use when fetching tokens. | ||
*/ | ||
constructor(clientApplication: ConfidentialClientApplication, appId: string, authority: string, scope: string); | ||
|
||
/** | ||
* Create an MsalAppCredentials instance using a confidential client application. | ||
* | ||
* @param appId The application ID. | ||
* @param appPassword The application password. | ||
* @param authority The authority to use for fetching tokens | ||
* @param scope The oauth scope to use when fetching tokens. | ||
*/ | ||
constructor(appId: string, appPassword: string, authority: string, scope: string); | ||
|
||
/** | ||
* Create an MsalAppCredentials instance using a confidential client application. | ||
* | ||
* @param appId The application ID. | ||
* @param certificate The client certificate details. | ||
* @param authority The authority to use for fetching tokens | ||
* @param scope The oauth scope to use when fetching tokens. | ||
*/ | ||
constructor(appId: string, certificate: Certificate, authority: string, scope: string); | ||
|
||
/** | ||
* @internal | ||
*/ | ||
constructor(); | ||
|
||
/** | ||
* @internal | ||
*/ | ||
constructor( | ||
maybeClientApplicationOrAppId?: ConfidentialClientApplication | string, | ||
maybeAppIdOrAppPasswordOrCertificate?: string | Certificate, | ||
maybeAuthority?: string, | ||
maybeScope?: string | ||
) { | ||
const appId = | ||
typeof maybeClientApplicationOrAppId === 'string' | ||
? maybeClientApplicationOrAppId | ||
: typeof maybeAppIdOrAppPasswordOrCertificate === 'string' | ||
? maybeAppIdOrAppPasswordOrCertificate | ||
: undefined; | ||
|
||
super(appId, undefined, maybeScope); | ||
|
||
if (typeof maybeClientApplicationOrAppId !== 'string') { | ||
this.clientApplication = maybeClientApplicationOrAppId; | ||
} else { | ||
const auth: NodeAuthOptions = { | ||
authority: maybeAuthority, | ||
clientId: appId, | ||
}; | ||
|
||
auth.clientCertificate = | ||
typeof maybeAppIdOrAppPasswordOrCertificate !== 'string' | ||
? maybeAppIdOrAppPasswordOrCertificate | ||
: undefined; | ||
|
||
auth.clientSecret = | ||
typeof maybeAppIdOrAppPasswordOrCertificate === 'string' | ||
? maybeAppIdOrAppPasswordOrCertificate | ||
: undefined; | ||
|
||
this.clientApplication = new ConfidentialClientApplication({ auth }); | ||
} | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
protected async refreshToken(): Promise<TokenResponse> { | ||
if (!this.clientApplication) { | ||
throw new Error('getToken should not be called for empty credentials.'); | ||
} | ||
|
||
const scopePostfix = '/.default'; | ||
let scope = this.oAuthScope; | ||
if (!scope.endsWith(scopePostfix)) { | ||
scope = `${scope}${scopePostfix}`; | ||
} | ||
|
||
const token = await this.clientApplication.acquireTokenByClientCredential({ | ||
scopes: [scope], | ||
skipCache: true, | ||
}); | ||
|
||
const { accessToken } = token ?? {}; | ||
if (typeof accessToken !== 'string') { | ||
throw new Error('Authentication: No access token received from MSAL.'); | ||
} | ||
|
||
const expiresIn = (token.expiresOn.getTime() - Date.now()) / 1000; | ||
|
||
return { | ||
accessToken: token.accessToken, | ||
expiresOn: token.expiresOn, | ||
tokenType: token.tokenType, | ||
expiresIn: expiresIn, | ||
resource: this.oAuthScope, | ||
}; | ||
} | ||
} |
84 changes: 84 additions & 0 deletions
84
libraries/botframework-connector/src/auth/msalServiceClientCredentialsFactory.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,84 @@ | ||
/** | ||
* @module botframework-connector | ||
*/ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import { ConfidentialClientApplication } from '@azure/msal-node'; | ||
import { MsalAppCredentials } from './msalAppCredentials'; | ||
import { ServiceClientCredentials } from '@azure/ms-rest-js'; | ||
import { ServiceClientCredentialsFactory } from './serviceClientCredentialsFactory'; | ||
import { AuthenticationConstants } from './authenticationConstants'; | ||
import { GovernmentConstants } from './governmentConstants'; | ||
|
||
/** | ||
* An implementation of ServiceClientCredentialsFactory that generates MsalAppCredentials | ||
*/ | ||
export class MsalServiceClientCredentialsFactory implements ServiceClientCredentialsFactory { | ||
private readonly appId: string; | ||
|
||
/** | ||
* Create an MsalServiceClientCredentialsFactory instance using runtime configuration and an | ||
* `@azure/msal-node` `ConfidentialClientApplication`. | ||
* | ||
* @param appId App ID for validation. | ||
* @param clientApplication An `@azure/msal-node` `ConfidentialClientApplication` instance. | ||
*/ | ||
constructor(appId: string, private readonly clientApplication: ConfidentialClientApplication) { | ||
this.appId = appId; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
async isValidAppId(appId: string): Promise<boolean> { | ||
return appId === this.appId; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
async isAuthenticationDisabled(): Promise<boolean> { | ||
return !this.appId; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
async createCredentials( | ||
appId: string, | ||
audience: string, | ||
loginEndpoint: string, | ||
_validateAuthority: boolean | ||
): Promise<ServiceClientCredentials> { | ||
if (await this.isAuthenticationDisabled()) { | ||
return MsalAppCredentials.Empty; | ||
} | ||
|
||
if (!(await this.isValidAppId(appId))) { | ||
throw new Error('Invalid appId.'); | ||
} | ||
|
||
const normalizedEndpoint = loginEndpoint.toLowerCase(); | ||
|
||
if (normalizedEndpoint.startsWith(AuthenticationConstants.ToChannelFromBotLoginUrlPrefix)) { | ||
return new MsalAppCredentials( | ||
this.clientApplication, | ||
appId, | ||
undefined, | ||
audience || AuthenticationConstants.ToBotFromChannelTokenIssuer | ||
); | ||
} | ||
|
||
if (normalizedEndpoint === GovernmentConstants.ToChannelFromBotLoginUrl.toLowerCase()) { | ||
return new MsalAppCredentials( | ||
this.clientApplication, | ||
appId, | ||
GovernmentConstants.ToChannelFromBotLoginUrl, | ||
audience || GovernmentConstants.ToChannelFromBotOAuthScope | ||
); | ||
} | ||
|
||
return new MsalAppCredentials(this.clientApplication, appId, loginEndpoint, audience); | ||
} | ||
} |
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
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 |
---|---|---|
|
@@ -72,9 +72,9 @@ | |
xml2js "^0.5.0" | ||
|
||
"@azure/core-lro@^2.2.0": | ||
version "2.5.3" | ||
resolved "https://registry.yarnpkg.com/@azure/core-lro/-/core-lro-2.5.3.tgz#6bb74e76dd84071d319abf7025e8abffef091f91" | ||
integrity sha512-ubkOf2YCnVtq7KqEJQqAI8dDD5rH1M6OP5kW0KO/JQyTaxLA0N0pjFWvvaysCj9eHMNBcuuoZXhhl0ypjod2DA== | ||
version "2.5.4" | ||
resolved "https://registry.yarnpkg.com/@azure/core-lro/-/core-lro-2.5.4.tgz#b21e2bcb8bd9a8a652ff85b61adeea51a8055f90" | ||
integrity sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q== | ||
dependencies: | ||
"@azure/abort-controller" "^1.0.0" | ||
"@azure/core-util" "^1.2.0" | ||
|
@@ -142,14 +142,22 @@ | |
dependencies: | ||
tslib "^2.0.0" | ||
|
||
"@azure/core-util@^1.1.1", "@azure/core-util@^1.2.0": | ||
"@azure/core-util@^1.1.1": | ||
version "1.3.2" | ||
resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.3.2.tgz#3f8cfda1e87fac0ce84f8c1a42fcd6d2a986632d" | ||
integrity sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ== | ||
dependencies: | ||
"@azure/abort-controller" "^1.0.0" | ||
tslib "^2.2.0" | ||
|
||
"@azure/core-util@^1.2.0": | ||
version "1.5.0" | ||
resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.5.0.tgz#ffe49c3e867044da67daeb8122143fa065e1eb0e" | ||
integrity sha512-GZBpVFDtQ/15hW1OgBcRdT4Bl7AEpcEZqLfbAvOtm1CQUncKWiYapFHVD588hmlV27NbOOtSm3cnLF3lvoHi4g== | ||
dependencies: | ||
"@azure/abort-controller" "^1.0.0" | ||
tslib "^2.2.0" | ||
|
||
"@azure/[email protected]": | ||
version "3.10.0" | ||
resolved "https://registry.yarnpkg.com/@azure/cosmos/-/cosmos-3.10.0.tgz#ec11828e380a656f689357b51e8f3f451d78640d" | ||
|
@@ -235,6 +243,11 @@ | |
dependencies: | ||
"@azure/msal-common" "^5.0.0" | ||
|
||
"@azure/[email protected]": | ||
version "13.3.0" | ||
resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-13.3.0.tgz#dfa39810e0fbce6e07ca85a2cf305da58d30b7c9" | ||
integrity sha512-/VFWTicjcJbrGp3yQP7A24xU95NiDMe23vxIU1U6qdRPFsprMDNUohMudclnd+WSHE4/McqkZs/nUU3sAKkVjg== | ||
|
||
"@azure/msal-common@^4.5.1": | ||
version "4.5.1" | ||
resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-4.5.1.tgz#f35af8b634ae24aebd0906deb237c0db1afa5826" | ||
|
@@ -249,6 +262,15 @@ | |
dependencies: | ||
debug "^4.1.1" | ||
|
||
"@azure/msal-node@^1.2.0": | ||
version "1.18.3" | ||
resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-1.18.3.tgz#e265556d4db0340590eeab5341469fb6740251d0" | ||
integrity sha512-lI1OsxNbS/gxRD4548Wyj22Dk8kS7eGMwD9GlBZvQmFV8FJUXoXySL1BiNzDsHUE96/DS/DHmA+F73p1Dkcktg== | ||
dependencies: | ||
"@azure/msal-common" "13.3.0" | ||
jsonwebtoken "^9.0.0" | ||
uuid "^8.3.0" | ||
|
||
"@azure/msal-node@^1.3.0": | ||
version "1.3.1" | ||
resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-1.3.1.tgz#55c8915c9bc5222dbe152ffd67f9357b83461fde" | ||
|