-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2112 from AzureAD/msal-node/client-credential-grant
[msal-node][msal-common] Adds support for client credential grant
- Loading branch information
Showing
19 changed files
with
14,077 additions
and
58 deletions.
There are no files selected for viewing
8 changes: 8 additions & 0 deletions
8
change/@azure-msal-common-2020-08-14-17-01-57-msal-node-client-credential-grant.json
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,8 @@ | ||
{ | ||
"type": "minor", | ||
"comment": "Add support for acquiring tokens with client credentials grant", | ||
"packageName": "@azure/msal-common", | ||
"email": "sagonzal@microsoft.com", | ||
"dependentChangeType": "patch", | ||
"date": "2020-08-15T00:00:57.341Z" | ||
} |
7 changes: 7 additions & 0 deletions
7
change/@azure-msal-node-2020-08-14-17-01-57-msal-node-client-credential-grant.json
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,7 @@ | ||
{ | ||
"type": "prerelease", | ||
"packageName": "@azure/msal-node", | ||
"email": "sagonzal@microsoft.com", | ||
"dependentChangeType": "patch", | ||
"date": "2020-08-15T00:01:24.544Z" | ||
} |
8 changes: 8 additions & 0 deletions
8
...e/msal-node-client-credentials-2020-08-14-17-01-57-msal-node-client-credential-grant.json
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,8 @@ | ||
{ | ||
"type": "none", | ||
"comment": "Add client credential sample", | ||
"packageName": "msal-node-client-credentials", | ||
"email": "sagonzal@microsoft.com", | ||
"dependentChangeType": "patch", | ||
"date": "2020-08-15T00:01:57.173Z" | ||
} |
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,129 @@ | ||
/* | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. | ||
*/ | ||
|
||
import { ClientConfiguration } from "../config/ClientConfiguration"; | ||
import { BaseClient } from "./BaseClient"; | ||
import { Authority } from "../authority/Authority"; | ||
import { RequestParameterBuilder } from "../request/RequestParameterBuilder"; | ||
import { ScopeSet } from "../request/ScopeSet"; | ||
import { GrantType } from "../utils/Constants"; | ||
import { ResponseHandler } from "../response/ResponseHandler"; | ||
import { AuthenticationResult } from "../response/AuthenticationResult"; | ||
import { ClientCredentialRequest } from "../request/ClientCredentialRequest"; | ||
import { CredentialFilter, CredentialCache } from "../cache/utils/CacheTypes"; | ||
import { CredentialType } from "../utils/Constants"; | ||
import { AccessTokenEntity } from "../cache/entities/AccessTokenEntity"; | ||
import { TimeUtils } from "../utils/TimeUtils"; | ||
|
||
/** | ||
* OAuth2.0 client credential grant | ||
*/ | ||
export class ClientCredentialClient extends BaseClient { | ||
|
||
private scopeSet: ScopeSet; | ||
|
||
constructor(configuration: ClientConfiguration) { | ||
super(configuration); | ||
} | ||
|
||
public async acquireToken(request: ClientCredentialRequest): Promise<AuthenticationResult> { | ||
|
||
this.scopeSet = new ScopeSet(request.scopes || []); | ||
|
||
if (request.skipCache) { | ||
return await this.executeTokenRequest(request, this.authority); | ||
} | ||
|
||
const cachedAuthenticationResult = this.getCachedAuthenticationResult(); | ||
if (cachedAuthenticationResult != null) { | ||
return cachedAuthenticationResult; | ||
} else { | ||
return await this.executeTokenRequest(request, this.authority); | ||
} | ||
} | ||
|
||
private getCachedAuthenticationResult(): AuthenticationResult { | ||
const cachedAccessToken = this.readAccessTokenFromCache(); | ||
if (!cachedAccessToken || | ||
TimeUtils.isTokenExpired(cachedAccessToken.expiresOn, this.config.systemOptions.tokenRenewalOffsetSeconds)) { | ||
return null; | ||
} | ||
return ResponseHandler.generateAuthenticationResult({ | ||
account: null, | ||
accessToken: cachedAccessToken, | ||
idToken: null, | ||
refreshToken: null | ||
}, null, true); | ||
} | ||
|
||
private readAccessTokenFromCache(): AccessTokenEntity { | ||
const accessTokenFilter: CredentialFilter = { | ||
homeAccountId: "", | ||
environment: this.authority.canonicalAuthorityUrlComponents.HostNameAndPort, | ||
credentialType: CredentialType.ACCESS_TOKEN, | ||
clientId: this.config.authOptions.clientId, | ||
realm: this.authority.tenant, | ||
target: this.scopeSet.printScopesLowerCase() | ||
}; | ||
const credentialCache: CredentialCache = this.cacheManager.getCredentialsFilteredBy(accessTokenFilter); | ||
const accessTokens = Object.keys(credentialCache.accessTokens).map(key => credentialCache.accessTokens[key]); | ||
if (accessTokens.length < 1) { | ||
return null; | ||
} | ||
return accessTokens[0] as AccessTokenEntity; | ||
} | ||
|
||
private async executeTokenRequest(request: ClientCredentialRequest, authority: Authority) | ||
: Promise<AuthenticationResult> { | ||
|
||
const requestBody = this.createTokenRequestBody(request); | ||
const headers: Record<string, string> = this.createDefaultTokenRequestHeaders(); | ||
|
||
const response = await this.executePostToTokenEndpoint(authority.tokenEndpoint, requestBody, headers); | ||
|
||
const responseHandler = new ResponseHandler( | ||
this.config.authOptions.clientId, | ||
this.cacheManager, | ||
this.cryptoUtils, | ||
this.logger | ||
); | ||
|
||
responseHandler.validateTokenResponse(response.body); | ||
const tokenResponse = responseHandler.handleServerTokenResponse( | ||
response.body, | ||
this.authority, | ||
null, | ||
null, | ||
request.scopes | ||
); | ||
|
||
return tokenResponse; | ||
} | ||
|
||
private createTokenRequestBody(request: ClientCredentialRequest): string { | ||
const parameterBuilder = new RequestParameterBuilder(); | ||
|
||
parameterBuilder.addClientId(this.config.authOptions.clientId); | ||
|
||
parameterBuilder.addScopes(this.scopeSet); | ||
|
||
parameterBuilder.addGrantType(GrantType.CLIENT_CREDENTIALS_GRANT); | ||
|
||
const correlationId = request.correlationId || this.config.cryptoInterface.createNewGuid(); | ||
parameterBuilder.addCorrelationId(correlationId); | ||
|
||
if (this.config.clientCredentials.clientSecret) { | ||
parameterBuilder.addClientSecret(this.config.clientCredentials.clientSecret); | ||
} | ||
|
||
if (this.config.clientCredentials.clientAssertion) { | ||
const clientAssertion = this.config.clientCredentials.clientAssertion; | ||
parameterBuilder.addClientAssertion(clientAssertion.assertion); | ||
parameterBuilder.addClientAssertionType(clientAssertion.assertionType); | ||
} | ||
|
||
return parameterBuilder.createQueryString(); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/* | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. | ||
*/ | ||
|
||
import { BaseAuthRequest } from "./BaseAuthRequest"; | ||
|
||
/** | ||
* RefreshTokenRequest | ||
* - scopes - Array of scopes the application is requesting access to. | ||
* - authority - URL of the authority, the security token service (STS) from which MSAL will acquire tokens. | ||
* - correlationId - Unique GUID set per request to trace a request end-to-end for telemetry purposes. | ||
* - skipCache - Skip token cache lookup and force request to authority to get a a new token. Defaults to false. | ||
*/ | ||
export type ClientCredentialRequest = BaseAuthRequest & { | ||
skipCache?: boolean; | ||
}; |
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
Oops, something went wrong.