mirror of
https://github.com/MeowLynxSea/Proksea.git
synced 2025-07-11 03:44:39 +00:00
152 lines
7.6 KiB
JavaScript
152 lines
7.6 KiB
JavaScript
/*! @azure/msal-common v14.12.0 2024-06-10 */
|
|
'use strict';
|
|
import { BaseClient } from './BaseClient.mjs';
|
|
import { wasClockTurnedBack, isTokenExpired } from '../utils/TimeUtils.mjs';
|
|
import { RefreshTokenClient } from './RefreshTokenClient.mjs';
|
|
import { ClientAuthError, createClientAuthError } from '../error/ClientAuthError.mjs';
|
|
import { ResponseHandler } from '../response/ResponseHandler.mjs';
|
|
import { OIDC_DEFAULT_SCOPES, CacheOutcome } from '../utils/Constants.mjs';
|
|
import { StringUtils } from '../utils/StringUtils.mjs';
|
|
import { extractTokenClaims, checkMaxAge } from '../account/AuthToken.mjs';
|
|
import { PerformanceEvents } from '../telemetry/performance/PerformanceEvent.mjs';
|
|
import { invokeAsync } from '../utils/FunctionWrappers.mjs';
|
|
import { getTenantFromAuthorityString } from '../authority/Authority.mjs';
|
|
import { tokenRefreshRequired, noAccountInSilentRequest, authTimeNotFound } from '../error/ClientAuthErrorCodes.mjs';
|
|
|
|
/*
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License.
|
|
*/
|
|
/** @internal */
|
|
class SilentFlowClient extends BaseClient {
|
|
constructor(configuration, performanceClient) {
|
|
super(configuration, performanceClient);
|
|
}
|
|
/**
|
|
* Retrieves a token from cache if it is still valid, or uses the cached refresh token to renew
|
|
* the given token and returns the renewed token
|
|
* @param request
|
|
*/
|
|
async acquireToken(request) {
|
|
try {
|
|
const [authResponse, cacheOutcome] = await this.acquireCachedToken({
|
|
...request,
|
|
scopes: request.scopes?.length
|
|
? request.scopes
|
|
: [...OIDC_DEFAULT_SCOPES],
|
|
});
|
|
// if the token is not expired but must be refreshed; get a new one in the background
|
|
if (cacheOutcome === CacheOutcome.PROACTIVELY_REFRESHED) {
|
|
this.logger.info("SilentFlowClient:acquireCachedToken - Cached access token's refreshOn property has been exceeded'. It's not expired, but must be refreshed.");
|
|
// refresh the access token in the background
|
|
const refreshTokenClient = new RefreshTokenClient(this.config, this.performanceClient);
|
|
refreshTokenClient
|
|
.acquireTokenByRefreshToken(request)
|
|
.catch(() => {
|
|
// do nothing, this is running in the background and no action is to be taken upon success or failure
|
|
});
|
|
}
|
|
// return the cached token
|
|
return authResponse;
|
|
}
|
|
catch (e) {
|
|
if (e instanceof ClientAuthError &&
|
|
e.errorCode === tokenRefreshRequired) {
|
|
const refreshTokenClient = new RefreshTokenClient(this.config, this.performanceClient);
|
|
return refreshTokenClient.acquireTokenByRefreshToken(request);
|
|
}
|
|
else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Retrieves token from cache or throws an error if it must be refreshed.
|
|
* @param request
|
|
*/
|
|
async acquireCachedToken(request) {
|
|
this.performanceClient?.addQueueMeasurement(PerformanceEvents.SilentFlowClientAcquireCachedToken, request.correlationId);
|
|
let lastCacheOutcome = CacheOutcome.NOT_APPLICABLE;
|
|
if (request.forceRefresh ||
|
|
(!this.config.cacheOptions.claimsBasedCachingEnabled &&
|
|
!StringUtils.isEmptyObj(request.claims))) {
|
|
// Must refresh due to present force_refresh flag.
|
|
this.setCacheOutcome(CacheOutcome.FORCE_REFRESH_OR_CLAIMS, request.correlationId);
|
|
throw createClientAuthError(tokenRefreshRequired);
|
|
}
|
|
// We currently do not support silent flow for account === null use cases; This will be revisited for confidential flow usecases
|
|
if (!request.account) {
|
|
throw createClientAuthError(noAccountInSilentRequest);
|
|
}
|
|
const requestTenantId = request.account.tenantId ||
|
|
getTenantFromAuthorityString(request.authority);
|
|
const tokenKeys = this.cacheManager.getTokenKeys();
|
|
const cachedAccessToken = this.cacheManager.getAccessToken(request.account, request, tokenKeys, requestTenantId, this.performanceClient, request.correlationId);
|
|
if (!cachedAccessToken) {
|
|
// must refresh due to non-existent access_token
|
|
this.setCacheOutcome(CacheOutcome.NO_CACHED_ACCESS_TOKEN, request.correlationId);
|
|
throw createClientAuthError(tokenRefreshRequired);
|
|
}
|
|
else if (wasClockTurnedBack(cachedAccessToken.cachedAt) ||
|
|
isTokenExpired(cachedAccessToken.expiresOn, this.config.systemOptions.tokenRenewalOffsetSeconds)) {
|
|
// must refresh due to the expires_in value
|
|
this.setCacheOutcome(CacheOutcome.CACHED_ACCESS_TOKEN_EXPIRED, request.correlationId);
|
|
throw createClientAuthError(tokenRefreshRequired);
|
|
}
|
|
else if (cachedAccessToken.refreshOn &&
|
|
isTokenExpired(cachedAccessToken.refreshOn, 0)) {
|
|
// must refresh (in the background) due to the refresh_in value
|
|
lastCacheOutcome = CacheOutcome.PROACTIVELY_REFRESHED;
|
|
// don't throw ClientAuthError.createRefreshRequiredError(), return cached token instead
|
|
}
|
|
const environment = request.authority || this.authority.getPreferredCache();
|
|
const cacheRecord = {
|
|
account: this.cacheManager.readAccountFromCache(request.account),
|
|
accessToken: cachedAccessToken,
|
|
idToken: this.cacheManager.getIdToken(request.account, tokenKeys, requestTenantId, this.performanceClient, request.correlationId),
|
|
refreshToken: null,
|
|
appMetadata: this.cacheManager.readAppMetadataFromCache(environment),
|
|
};
|
|
this.setCacheOutcome(lastCacheOutcome, request.correlationId);
|
|
if (this.config.serverTelemetryManager) {
|
|
this.config.serverTelemetryManager.incrementCacheHits();
|
|
}
|
|
return [
|
|
await invokeAsync(this.generateResultFromCacheRecord.bind(this), PerformanceEvents.SilentFlowClientGenerateResultFromCacheRecord, this.logger, this.performanceClient, request.correlationId)(cacheRecord, request),
|
|
lastCacheOutcome,
|
|
];
|
|
}
|
|
setCacheOutcome(cacheOutcome, correlationId) {
|
|
this.serverTelemetryManager?.setCacheOutcome(cacheOutcome);
|
|
this.performanceClient?.addFields({
|
|
cacheOutcome: cacheOutcome,
|
|
}, correlationId);
|
|
if (cacheOutcome !== CacheOutcome.NOT_APPLICABLE) {
|
|
this.logger.info(`Token refresh is required due to cache outcome: ${cacheOutcome}`);
|
|
}
|
|
}
|
|
/**
|
|
* Helper function to build response object from the CacheRecord
|
|
* @param cacheRecord
|
|
*/
|
|
async generateResultFromCacheRecord(cacheRecord, request) {
|
|
this.performanceClient?.addQueueMeasurement(PerformanceEvents.SilentFlowClientGenerateResultFromCacheRecord, request.correlationId);
|
|
let idTokenClaims;
|
|
if (cacheRecord.idToken) {
|
|
idTokenClaims = extractTokenClaims(cacheRecord.idToken.secret, this.config.cryptoInterface.base64Decode);
|
|
}
|
|
// token max_age check
|
|
if (request.maxAge || request.maxAge === 0) {
|
|
const authTime = idTokenClaims?.auth_time;
|
|
if (!authTime) {
|
|
throw createClientAuthError(authTimeNotFound);
|
|
}
|
|
checkMaxAge(authTime, request.maxAge);
|
|
}
|
|
return ResponseHandler.generateAuthenticationResult(this.cryptoUtils, this.authority, cacheRecord, true, request, idTokenClaims);
|
|
}
|
|
}
|
|
|
|
export { SilentFlowClient };
|
|
//# sourceMappingURL=SilentFlowClient.mjs.map
|