LookAtMySuitBot/js/node_modules/@azure/msal-node/dist/client/OnBehalfOfClient.mjs

211 lines
10 KiB
JavaScript
Raw Normal View History

2023-12-24 20:08:39 -05:00
/*! @azure/msal-node v2.5.1 2023-11-07 */
'use strict';
import { BaseClient, ScopeSet, CacheOutcome, createClientAuthError, ClientAuthErrorCodes, TimeUtils, AuthToken, ResponseHandler, AuthenticationScheme, CredentialType, UrlString, RequestParameterBuilder, GrantType, AADServerParamKeys, Constants } from '@azure/msal-common';
import { EncodingUtils } from '../utils/EncodingUtils.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* On-Behalf-Of client
*/
class OnBehalfOfClient extends BaseClient {
constructor(configuration) {
super(configuration);
}
/**
* Public API to acquire tokens with on behalf of flow
* @param request
*/
async acquireToken(request) {
this.scopeSet = new ScopeSet(request.scopes || []);
// generate the user_assertion_hash for OBOAssertion
this.userAssertionHash = await this.cryptoUtils.hashString(request.oboAssertion);
if (request.skipCache) {
return await this.executeTokenRequest(request, this.authority, this.userAssertionHash);
}
try {
return await this.getCachedAuthenticationResult(request);
}
catch (e) {
// Any failure falls back to interactive request, once we implement distributed cache, we plan to handle `createRefreshRequiredError` to refresh using the RT
return await this.executeTokenRequest(request, this.authority, this.userAssertionHash);
}
}
/**
* look up cache for tokens
* Find idtoken in the cache
* Find accessToken based on user assertion and account info in the cache
* Please note we are not yet supported OBO tokens refreshed with long lived RT. User will have to send a new assertion if the current access token expires
* This is to prevent security issues when the assertion changes over time, however, longlived RT helps retaining the session
* @param request
*/
async getCachedAuthenticationResult(request) {
// look in the cache for the access_token which matches the incoming_assertion
const cachedAccessToken = this.readAccessTokenFromCacheForOBO(this.config.authOptions.clientId, request);
if (!cachedAccessToken) {
// Must refresh due to non-existent access_token.
this.serverTelemetryManager?.setCacheOutcome(CacheOutcome.NO_CACHED_ACCESS_TOKEN);
this.logger.info("SilentFlowClient:acquireCachedToken - No access token found in cache for the given properties.");
throw createClientAuthError(ClientAuthErrorCodes.tokenRefreshRequired);
}
else if (TimeUtils.isTokenExpired(cachedAccessToken.expiresOn, this.config.systemOptions.tokenRenewalOffsetSeconds)) {
// Access token expired, will need to renewed
this.serverTelemetryManager?.setCacheOutcome(CacheOutcome.CACHED_ACCESS_TOKEN_EXPIRED);
this.logger.info(`OnbehalfofFlow:getCachedAuthenticationResult - Cached access token is expired or will expire within ${this.config.systemOptions.tokenRenewalOffsetSeconds} seconds.`);
throw createClientAuthError(ClientAuthErrorCodes.tokenRefreshRequired);
}
// fetch the idToken from cache
const cachedIdToken = this.readIdTokenFromCacheForOBO(cachedAccessToken.homeAccountId);
let idTokenClaims;
let cachedAccount = null;
if (cachedIdToken) {
idTokenClaims = AuthToken.extractTokenClaims(cachedIdToken.secret, EncodingUtils.base64Decode);
const localAccountId = idTokenClaims.oid || idTokenClaims.sub;
const accountInfo = {
homeAccountId: cachedIdToken.homeAccountId,
environment: cachedIdToken.environment,
tenantId: cachedIdToken.realm,
username: Constants.EMPTY_STRING,
localAccountId: localAccountId || Constants.EMPTY_STRING,
};
cachedAccount = this.cacheManager.readAccountFromCache(accountInfo);
}
// increment telemetry cache hit counter
if (this.config.serverTelemetryManager) {
this.config.serverTelemetryManager.incrementCacheHits();
}
return await ResponseHandler.generateAuthenticationResult(this.cryptoUtils, this.authority, {
account: cachedAccount,
accessToken: cachedAccessToken,
idToken: cachedIdToken,
refreshToken: null,
appMetadata: null,
}, true, request, idTokenClaims);
}
/**
* read idtoken from cache, this is a specific implementation for OBO as the requirements differ from a generic lookup in the cacheManager
* Certain use cases of OBO flow do not expect an idToken in the cache/or from the service
* @param atHomeAccountId {string}
*/
readIdTokenFromCacheForOBO(atHomeAccountId) {
const idTokenFilter = {
homeAccountId: atHomeAccountId,
environment: this.authority.canonicalAuthorityUrlComponents.HostNameAndPort,
credentialType: CredentialType.ID_TOKEN,
clientId: this.config.authOptions.clientId,
realm: this.authority.tenant,
};
const idTokens = this.cacheManager.getIdTokensByFilter(idTokenFilter);
// When acquiring a token on behalf of an application, there might not be an id token in the cache
if (idTokens.length < 1) {
return null;
}
return idTokens[0];
}
/**
* Fetches the cached access token based on incoming assertion
* @param clientId
* @param request
* @param userAssertionHash
*/
readAccessTokenFromCacheForOBO(clientId, request) {
const authScheme = request.authenticationScheme || AuthenticationScheme.BEARER;
/*
* Distinguish between Bearer and PoP/SSH token cache types
* Cast to lowercase to handle "bearer" from ADFS
*/
const credentialType = authScheme &&
authScheme.toLowerCase() !==
AuthenticationScheme.BEARER.toLowerCase()
? CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME
: CredentialType.ACCESS_TOKEN;
const accessTokenFilter = {
credentialType: credentialType,
clientId,
target: ScopeSet.createSearchScopes(this.scopeSet.asArray()),
tokenType: authScheme,
keyId: request.sshKid,
requestedClaimsHash: request.requestedClaimsHash,
userAssertionHash: this.userAssertionHash,
};
const accessTokens = this.cacheManager.getAccessTokensByFilter(accessTokenFilter);
const numAccessTokens = accessTokens.length;
if (numAccessTokens < 1) {
return null;
}
else if (numAccessTokens > 1) {
throw createClientAuthError(ClientAuthErrorCodes.multipleMatchingTokens);
}
return accessTokens[0];
}
/**
* Make a network call to the server requesting credentials
* @param request
* @param authority
*/
async executeTokenRequest(request, authority, userAssertionHash) {
const queryParametersString = this.createTokenQueryParameters(request);
const endpoint = UrlString.appendQueryString(authority.tokenEndpoint, queryParametersString);
const requestBody = this.createTokenRequestBody(request);
const headers = this.createTokenRequestHeaders();
const thumbprint = {
clientId: this.config.authOptions.clientId,
authority: request.authority,
scopes: request.scopes,
claims: request.claims,
authenticationScheme: request.authenticationScheme,
resourceRequestMethod: request.resourceRequestMethod,
resourceRequestUri: request.resourceRequestUri,
shrClaims: request.shrClaims,
sshKid: request.sshKid,
};
const reqTimestamp = TimeUtils.nowSeconds();
const response = await this.executePostToTokenEndpoint(endpoint, requestBody, headers, thumbprint, request.correlationId);
const responseHandler = new ResponseHandler(this.config.authOptions.clientId, this.cacheManager, this.cryptoUtils, this.logger, this.config.serializableCache, this.config.persistencePlugin);
responseHandler.validateTokenResponse(response.body);
const tokenResponse = await responseHandler.handleServerTokenResponse(response.body, this.authority, reqTimestamp, request, undefined, userAssertionHash);
return tokenResponse;
}
/**
* generate a server request in accepable format
* @param request
*/
createTokenRequestBody(request) {
const parameterBuilder = new RequestParameterBuilder();
parameterBuilder.addClientId(this.config.authOptions.clientId);
parameterBuilder.addScopes(request.scopes);
parameterBuilder.addGrantType(GrantType.JWT_BEARER);
parameterBuilder.addClientInfo();
parameterBuilder.addLibraryInfo(this.config.libraryInfo);
parameterBuilder.addApplicationTelemetry(this.config.telemetry.application);
parameterBuilder.addThrottling();
if (this.serverTelemetryManager) {
parameterBuilder.addServerTelemetry(this.serverTelemetryManager);
}
const correlationId = request.correlationId ||
this.config.cryptoInterface.createNewGuid();
parameterBuilder.addCorrelationId(correlationId);
parameterBuilder.addRequestTokenUse(AADServerParamKeys.ON_BEHALF_OF);
parameterBuilder.addOboAssertion(request.oboAssertion);
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);
}
if (request.claims ||
(this.config.authOptions.clientCapabilities &&
this.config.authOptions.clientCapabilities.length > 0)) {
parameterBuilder.addClaims(request.claims, this.config.authOptions.clientCapabilities);
}
return parameterBuilder.createQueryString();
}
}
export { OnBehalfOfClient };
//# sourceMappingURL=OnBehalfOfClient.mjs.map