UNPKG

@azure/msal-common

Version:
133 lines (130 loc) 6.97 kB
/*! @azure/msal-common v16.6.2 2026-05-19 */ 'use strict'; import { buildClientConfiguration } from '../config/ClientConfiguration.mjs'; import { wasClockTurnedBack, isTokenExpired } from '../utils/TimeUtils.mjs'; import { createClientAuthError } from '../error/ClientAuthError.mjs'; import { ResponseHandler } from '../response/ResponseHandler.mjs'; import { CacheOutcome } from '../utils/Constants.mjs'; import { StringUtils } from '../utils/StringUtils.mjs'; import { extractTokenClaims, checkMaxAge } from '../account/AuthToken.mjs'; import { SilentFlowClientGenerateResultFromCacheRecord } from '../telemetry/performance/PerformanceEvents.mjs'; import { invokeAsync } from '../utils/FunctionWrappers.mjs'; import { getTenantFromAuthorityString } from '../authority/Authority.mjs'; import { Logger } from '../logger/Logger.mjs'; import { name, version } from '../packageMetadata.mjs'; import { tokenRefreshRequired, noAccountInSilentRequest, authTimeNotFound } from '../error/ClientAuthErrorCodes.mjs'; /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ /** @internal */ class SilentFlowClient { constructor(configuration, performanceClient) { // Set the configuration this.config = buildClientConfiguration(configuration); // Initialize the logger this.logger = new Logger(this.config.loggerOptions, name, version); // Initialize crypto this.cryptoUtils = this.config.cryptoInterface; // Initialize storage interface this.cacheManager = this.config.storageInterface; // Set the network interface this.networkClient = this.config.networkInterface; // Set TelemetryManager this.serverTelemetryManager = this.config.serverTelemetryManager; // set Authority this.authority = this.config.authOptions.authority; // set performance telemetry client this.performanceClient = performanceClient; } /** * Retrieves token from cache or throws an error if it must be refreshed. * @param request */ async acquireCachedToken(request) { let lastCacheOutcome = CacheOutcome.NOT_APPLICABLE; if (request.forceRefresh || !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); 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 (request.resource) { // cached access token must have a resource that matches the request resource for MCP scenarios if (cachedAccessToken.resource !== request.resource) { this.setCacheOutcome(CacheOutcome.NO_CACHED_ACCESS_TOKEN, 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.getAccount(this.cacheManager.generateAccountKey(request.account), request.correlationId), accessToken: cachedAccessToken, idToken: this.cacheManager.getIdToken(request.account, request.correlationId, tokenKeys, requestTenantId), refreshToken: null, appMetadata: this.cacheManager.readAppMetadataFromCache(environment, request.correlationId), }; this.setCacheOutcome(lastCacheOutcome, request.correlationId); if (this.config.serverTelemetryManager) { this.config.serverTelemetryManager.incrementCacheHits(); } return [ await invokeAsync(this.generateResultFromCacheRecord.bind(this), 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}'`, correlationId); } } /** * Helper function to build response object from the CacheRecord * @param cacheRecord */ async generateResultFromCacheRecord(cacheRecord, request) { 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, this.performanceClient, idTokenClaims); } } export { SilentFlowClient }; //# sourceMappingURL=SilentFlowClient.mjs.map