UNPKG

@azure/msal-browser

Version:
227 lines (199 loc) 8.17 kB
/* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ import { CustomAuthAuthority } from "../../core/CustomAuthAuthority.js"; import { DefaultPackageInfo } from "../../CustomAuthConstants.js"; import * as PublicApiId from "../../core/telemetry/PublicApiId.js"; import { CustomAuthInteractionClientBase } from "../../core/interaction_client/CustomAuthInteractionClientBase.js"; import { AccountInfo, ClientAuthError, ClientAuthErrorCodes, ClientConfiguration, CommonSilentFlowRequest, RefreshTokenClient, ServerTelemetryManager, SilentFlowClient, UrlString, } from "@azure/msal-common/browser"; import { AuthenticationResult } from "../../../response/AuthenticationResult.js"; import { ClearCacheRequest } from "../../../request/ClearCacheRequest.js"; import { ApiId } from "../../../utils/BrowserConstants.js"; import { getCurrentUri } from "../../../utils/BrowserUtils.js"; import { clearCacheOnLogout, initializeServerTelemetryManager, } from "../../../interaction_client/BaseInteractionClient.js"; export class CustomAuthSilentCacheClient extends CustomAuthInteractionClientBase { /** * Acquires a token from the cache if it is not expired. Otherwise, makes a request to renew the token. * If forceRresh is set to false, then looks up the access token in cache first. * If access token is expired or not found, then uses refresh token to get a new access token. * If no refresh token is found or it is expired, then throws error. * If forceRefresh is set to true, then skips token cache lookup and fetches a new token using refresh token * If no refresh token is found or it is expired, then throws error. * @param silentRequest The silent request object. * @returns {Promise<AuthenticationResult>} The promise that resolves to an AuthenticationResult. */ override async acquireToken( silentRequest: CommonSilentFlowRequest ): Promise<AuthenticationResult> { const correlationId = silentRequest.correlationId || this.correlationId; const telemetryManager = initializeServerTelemetryManager( PublicApiId.ACCOUNT_GET_ACCESS_TOKEN, this.config.auth.clientId, correlationId, this.browserStorage, this.logger, silentRequest.forceRefresh ); const clientConfig = this.getCustomAuthClientConfiguration( telemetryManager, this.customAuthAuthority, correlationId ); const silentFlowClient = new SilentFlowClient( clientConfig, this.performanceClient ); try { this.logger.verbose( "Starting silent flow to acquire token from cache", correlationId ); const result = await silentFlowClient.acquireCachedToken( silentRequest ); this.logger.verbose( "Silent flow to acquire token from cache is completed and token is found", correlationId ); return result[0] as AuthenticationResult; } catch (error) { if ( error instanceof ClientAuthError && error.errorCode === ClientAuthErrorCodes.tokenRefreshRequired ) { this.logger.verbose( "Token refresh is required to acquire token silently", correlationId ); const refreshTokenClient = new RefreshTokenClient( clientConfig, this.performanceClient ); this.logger.verbose( "Starting refresh flow to refresh token", correlationId ); const refreshTokenResult = await refreshTokenClient.acquireTokenByRefreshToken( silentRequest, PublicApiId.ACCOUNT_GET_ACCESS_TOKEN ); this.logger.verbose( "Refresh flow to refresh token is completed", correlationId ); return refreshTokenResult as AuthenticationResult; } throw error; } } override async logout(logoutRequest?: ClearCacheRequest): Promise<void> { const validLogoutRequest = this.initializeLogoutRequest(logoutRequest); const correlationId = logoutRequest?.correlationId || this.correlationId; // Clear the cache this.logger.verbose("Start to clear the cache", correlationId); await clearCacheOnLogout( this.browserStorage, this.browserCrypto, this.logger, correlationId, validLogoutRequest?.account ); this.logger.verbose("Cache cleared", correlationId); const postLogoutRedirectUri = this.config.auth.postLogoutRedirectUri; if (postLogoutRedirectUri) { const absoluteRedirectUri = UrlString.getAbsoluteUrl( postLogoutRedirectUri, getCurrentUri() ); this.logger.verbose( "Post logout redirect uri is set, redirecting to uri", correlationId ); // Redirect to post logout redirect uri await this.navigationClient.navigateExternal(absoluteRedirectUri, { apiId: ApiId.logout, timeout: this.config.system.redirectNavigationTimeout, noHistory: false, }); } } getCurrentAccount(correlationId: string): AccountInfo | null { let account: AccountInfo | null = null; this.logger.verbose( "Getting the first account from cache.", correlationId ); const allAccounts = this.browserStorage.getAllAccounts( {}, correlationId ); if (allAccounts.length > 0) { if (allAccounts.length !== 1) { this.logger.warning( "Multiple accounts found in cache. This is not supported in the Native Auth scenario.", correlationId ); } account = allAccounts[0]; } if (account) { this.logger.verbose("Account data found.", correlationId); } else { this.logger.verbose("No account data found.", correlationId); } return account; } private getCustomAuthClientConfiguration( serverTelemetryManager: ServerTelemetryManager, customAuthAuthority: CustomAuthAuthority, correlationId: string ): ClientConfiguration { const logger = this.config.system.loggerOptions; return { authOptions: { clientId: this.config.auth.clientId, authority: customAuthAuthority, clientCapabilities: this.config.auth.clientCapabilities, redirectUri: this.config.auth.redirectUri, }, systemOptions: { tokenRenewalOffsetSeconds: this.config.system.tokenRenewalOffsetSeconds, preventCorsPreflight: true, }, loggerOptions: { loggerCallback: logger.loggerCallback, piiLoggingEnabled: logger.piiLoggingEnabled, logLevel: logger.logLevel, correlationId: correlationId, }, cryptoInterface: this.browserCrypto, networkInterface: this.networkClient, storageInterface: this.browserStorage, serverTelemetryManager: serverTelemetryManager, libraryInfo: { sku: DefaultPackageInfo.SKU, version: DefaultPackageInfo.VERSION, cpu: DefaultPackageInfo.CPU, os: DefaultPackageInfo.OS, }, telemetry: this.config.telemetry, }; } }