/*! @azure/msal-browser v5.7.0 2026-04-16 */
|
'use strict';
|
import { Constants, invokeAsync, ProtocolMode, AuthError, invoke, AuthorizeProtocol, PerformanceEvents } from '@azure/msal-common/browser';
|
import { StandardInteractionClient, initializeAuthorizationRequest } from './StandardInteractionClient.mjs';
|
import { StandardInteractionClientInitializeAuthorizationRequest, StandardInteractionClientCreateAuthCodeClient, SilentIframeClientTokenHelper, StandardInteractionClientGetDiscoveredAuthority, GenerateEarKey, GeneratePkceCodes, SilentHandlerInitiateAuthRequest, SilentHandlerMonitorIframeForHash, RemoveHiddenIframe, DeserializeResponse, HandleResponseCode, HandleResponseEar } from '../telemetry/BrowserPerformanceEvents.mjs';
|
import { createBrowserAuthError } from '../error/BrowserAuthError.mjs';
|
import { InteractionType, BrowserConstants } from '../utils/BrowserConstants.mjs';
|
import { initiateEarRequest, removeHiddenIframe, initiateCodeFlowWithPost, initiateCodeRequest } from '../interaction_handler/SilentHandler.mjs';
|
import { preconnect, waitForBridgeResponse } from '../utils/BrowserUtils.mjs';
|
import { deserializeResponse } from '../response/ResponseHandler.mjs';
|
import { handleResponseCode, handleResponseEAR, getAuthCodeRequestUrl } from '../protocol/Authorize.mjs';
|
import { generatePkceCodes } from '../crypto/PkceGenerator.mjs';
|
import { isPlatformAuthAllowed } from '../broker/nativeBroker/PlatformAuthProvider.mjs';
|
import { generateEarKey } from '../crypto/BrowserCrypto.mjs';
|
import { initializeServerTelemetryManager, getDiscoveredAuthority } from './BaseInteractionClient.mjs';
|
import { silentLogoutUnsupported } from '../error/BrowserAuthErrorCodes.mjs';
|
|
/*
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Licensed under the MIT License.
|
*/
|
class SilentIframeClient extends StandardInteractionClient {
|
constructor(config, storageImpl, browserCrypto, logger, eventHandler, navigationClient, apiId, performanceClient, nativeStorageImpl, correlationId, platformAuthProvider) {
|
super(config, storageImpl, browserCrypto, logger, eventHandler, navigationClient, performanceClient, correlationId, platformAuthProvider);
|
this.apiId = apiId;
|
this.nativeStorage = nativeStorageImpl;
|
}
|
/**
|
* Acquires a token silently by opening a hidden iframe to the /authorize endpoint with prompt=none or prompt=no_session
|
* @param request
|
*/
|
async acquireToken(request) {
|
// Check that we have some SSO data
|
if (!request.loginHint &&
|
!request.sid &&
|
(!request.account || !request.account.username)) {
|
this.logger.warning("1kl318", this.correlationId);
|
}
|
// Check the prompt value
|
const inputRequest = { ...request };
|
if (inputRequest.prompt) {
|
if (inputRequest.prompt !== Constants.PromptValue.NONE &&
|
inputRequest.prompt !== Constants.PromptValue.NO_SESSION) {
|
this.logger.warning("0bmctg", this.correlationId);
|
inputRequest.prompt = Constants.PromptValue.NONE;
|
}
|
}
|
else {
|
inputRequest.prompt = Constants.PromptValue.NONE;
|
}
|
// Create silent request
|
const silentRequest = await invokeAsync(initializeAuthorizationRequest, StandardInteractionClientInitializeAuthorizationRequest, this.logger, this.performanceClient, this.correlationId)(inputRequest, InteractionType.Silent, this.config, this.browserCrypto, this.browserStorage, this.logger, this.performanceClient, this.correlationId);
|
silentRequest.platformBroker = isPlatformAuthAllowed(this.config, this.logger, this.correlationId, this.platformAuthProvider, silentRequest.authenticationScheme);
|
preconnect(silentRequest.authority);
|
if (this.config.system.protocolMode === ProtocolMode.EAR) {
|
return this.executeEarFlow(silentRequest);
|
}
|
else {
|
return this.executeCodeFlow(silentRequest);
|
}
|
}
|
/**
|
* Executes auth code + PKCE flow
|
* @param request
|
* @returns
|
*/
|
async executeCodeFlow(request) {
|
let authClient;
|
const serverTelemetryManager = initializeServerTelemetryManager(this.apiId, this.config.auth.clientId, this.correlationId, this.browserStorage, this.logger);
|
try {
|
// Initialize the client
|
authClient = await invokeAsync(this.createAuthCodeClient.bind(this), StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, request.correlationId)({
|
serverTelemetryManager,
|
requestAuthority: request.authority,
|
requestAzureCloudOptions: request.azureCloudOptions,
|
requestExtraQueryParameters: request.extraQueryParameters,
|
account: request.account,
|
});
|
return await invokeAsync(this.silentTokenHelper.bind(this), SilentIframeClientTokenHelper, this.logger, this.performanceClient, request.correlationId)(authClient, request);
|
}
|
catch (e) {
|
if (e instanceof AuthError) {
|
e.setCorrelationId(this.correlationId);
|
serverTelemetryManager.cacheFailedRequest(e);
|
}
|
if (!authClient ||
|
!(e instanceof AuthError) ||
|
e.errorCode !== BrowserConstants.INVALID_GRANT_ERROR) {
|
throw e;
|
}
|
this.performanceClient.addFields({
|
retryError: e.errorCode,
|
}, this.correlationId);
|
return await invokeAsync(this.silentTokenHelper.bind(this), SilentIframeClientTokenHelper, this.logger, this.performanceClient, this.correlationId)(authClient, request);
|
}
|
}
|
/**
|
* Executes EAR flow
|
* @param request
|
*/
|
async executeEarFlow(request) {
|
const { correlationId, authority, azureCloudOptions, extraQueryParameters, account, } = request;
|
const discoveredAuthority = await invokeAsync(getDiscoveredAuthority, StandardInteractionClientGetDiscoveredAuthority, this.logger, this.performanceClient, correlationId)(this.config, this.correlationId, this.performanceClient, this.browserStorage, this.logger, authority, azureCloudOptions, extraQueryParameters, account);
|
const earJwk = await invokeAsync(generateEarKey, GenerateEarKey, this.logger, this.performanceClient, correlationId)();
|
const pkceCodes = await invokeAsync(generatePkceCodes, GeneratePkceCodes, this.logger, this.performanceClient, correlationId)(this.performanceClient, this.logger, correlationId);
|
const silentRequest = {
|
...request,
|
earJwk: earJwk,
|
codeChallenge: pkceCodes.challenge,
|
};
|
const iframe = await invokeAsync(initiateEarRequest, SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(this.config, discoveredAuthority, silentRequest, this.logger, this.performanceClient);
|
const responseType = this.config.auth.OIDCOptions.responseMode;
|
let responseString;
|
try {
|
responseString = await invokeAsync(waitForBridgeResponse, SilentHandlerMonitorIframeForHash, this.logger, this.performanceClient, correlationId)(this.config.system.iframeBridgeTimeout, this.logger, this.browserCrypto, request, this.performanceClient, this.config.experimental);
|
}
|
finally {
|
invoke(removeHiddenIframe, RemoveHiddenIframe, this.logger, this.performanceClient, correlationId)(iframe);
|
}
|
const serverParams = invoke(deserializeResponse, DeserializeResponse, this.logger, this.performanceClient, correlationId)(responseString, responseType, this.logger, this.correlationId);
|
if (!serverParams.ear_jwe && serverParams.code) {
|
// If server doesn't support EAR, they may fallback to auth code flow instead
|
const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, correlationId)({
|
serverTelemetryManager: initializeServerTelemetryManager(this.apiId, this.config.auth.clientId, correlationId, this.browserStorage, this.logger),
|
requestAuthority: request.authority,
|
requestAzureCloudOptions: request.azureCloudOptions,
|
requestExtraQueryParameters: request.extraQueryParameters,
|
account: request.account,
|
authority: discoveredAuthority,
|
});
|
return invokeAsync(handleResponseCode, HandleResponseCode, this.logger, this.performanceClient, correlationId)(silentRequest, serverParams, pkceCodes.verifier, this.apiId, this.config, authClient, this.browserStorage, this.nativeStorage, this.eventHandler, this.logger, this.performanceClient, this.platformAuthProvider);
|
}
|
else {
|
return invokeAsync(handleResponseEAR, HandleResponseEar, this.logger, this.performanceClient, correlationId)(silentRequest, serverParams, this.apiId, this.config, discoveredAuthority, this.browserStorage, this.nativeStorage, this.eventHandler, this.logger, this.performanceClient, this.platformAuthProvider);
|
}
|
}
|
/**
|
* Verifies SSO capability by making an iframe request to /authorize without exchanging the code for tokens.
|
* This is useful for verifying SSO capability in the background without the overhead of a full token exchange.
|
* @param request - The SSO silent request
|
* @returns true if SSO verification was successful with a valid authorization code, false otherwise
|
*/
|
async verifySso(request) {
|
const inputRequest = { ...request };
|
if (!inputRequest.prompt) {
|
inputRequest.prompt = Constants.PromptValue.NONE;
|
}
|
// Create silent request
|
const silentRequest = await invokeAsync(initializeAuthorizationRequest, StandardInteractionClientInitializeAuthorizationRequest, this.logger, this.performanceClient, this.correlationId)(inputRequest, InteractionType.Silent, this.config, this.browserCrypto, this.browserStorage, this.logger, this.performanceClient, this.correlationId);
|
const authClient = await invokeAsync(this.createAuthCodeClient.bind(this), StandardInteractionClientCreateAuthCodeClient, this.logger, this.performanceClient, this.correlationId)({
|
serverTelemetryManager: initializeServerTelemetryManager(this.apiId, this.config.auth.clientId, this.correlationId, this.browserStorage, this.logger),
|
requestAuthority: silentRequest.authority,
|
requestAzureCloudOptions: silentRequest.azureCloudOptions,
|
requestExtraQueryParameters: silentRequest.extraQueryParameters,
|
account: silentRequest.account,
|
});
|
const { serverParams } = await this.silentAuthorizeHelper(authClient, silentRequest);
|
const correlationId = silentRequest.correlationId;
|
// Validate the response - this checks for errors and validates state
|
AuthorizeProtocol.validateAuthorizationResponse(serverParams, silentRequest.state);
|
// Verify a valid authorization code is present
|
if (!serverParams.code) {
|
this.logger.warning("0y34ti", correlationId);
|
return false;
|
}
|
this.logger.verbose("0kkkcj", correlationId);
|
return true;
|
}
|
/**
|
* Currently Unsupported
|
*/
|
logout() {
|
// Synchronous so we must reject
|
return Promise.reject(createBrowserAuthError(silentLogoutUnsupported));
|
}
|
/**
|
* Helper which acquires an authorization code silently using a hidden iframe from given url
|
* using the scopes requested as part of the id, and exchanges the code for a set of OAuth tokens.
|
* @param navigateUrl
|
* @param userRequestScopes
|
*/
|
async silentTokenHelper(authClient, request) {
|
const { serverParams, pkceCodes } = await this.silentAuthorizeHelper(authClient, request);
|
return invokeAsync(handleResponseCode, HandleResponseCode, this.logger, this.performanceClient, request.correlationId)(request, serverParams, pkceCodes.verifier, this.apiId, this.config, authClient, this.browserStorage, this.nativeStorage, this.eventHandler, this.logger, this.performanceClient, this.platformAuthProvider);
|
}
|
/**
|
* Shared helper that generates PKCE codes, builds the /authorize URL,
|
* loads it in a hidden iframe, waits for the redirect-bridge response,
|
* and returns the deserialized server parameters along with the PKCE codes
|
* and the request that was sent.
|
*/
|
async silentAuthorizeHelper(authClient, request) {
|
const correlationId = request.correlationId;
|
const pkceCodes = await invokeAsync(generatePkceCodes, GeneratePkceCodes, this.logger, this.performanceClient, correlationId)(this.performanceClient, this.logger, correlationId);
|
const silentRequest = {
|
...request,
|
codeChallenge: pkceCodes.challenge,
|
};
|
let iframe;
|
if (request.httpMethod === Constants.HttpMethod.POST) {
|
iframe = await invokeAsync(initiateCodeFlowWithPost, SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(this.config, authClient.authority, silentRequest, this.logger, this.performanceClient);
|
}
|
else {
|
// Create authorize request url
|
const navigateUrl = await invokeAsync(getAuthCodeRequestUrl, PerformanceEvents.GetAuthCodeUrl, this.logger, this.performanceClient, correlationId)(this.config, authClient.authority, silentRequest, this.logger, this.performanceClient);
|
// Get the frame handle for the silent request
|
iframe = await invokeAsync(initiateCodeRequest, SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(navigateUrl, this.performanceClient, this.logger, correlationId);
|
}
|
const responseType = this.config.auth.OIDCOptions.responseMode;
|
// Wait for response from the redirect bridge.
|
let responseString;
|
try {
|
responseString = await invokeAsync(waitForBridgeResponse, SilentHandlerMonitorIframeForHash, this.logger, this.performanceClient, correlationId)(this.config.system.iframeBridgeTimeout, this.logger, this.browserCrypto, request, this.performanceClient, this.config.experimental);
|
}
|
finally {
|
invoke(removeHiddenIframe, RemoveHiddenIframe, this.logger, this.performanceClient, correlationId)(iframe);
|
}
|
const serverParams = invoke(deserializeResponse, DeserializeResponse, this.logger, this.performanceClient, correlationId)(responseString, responseType, this.logger, this.correlationId);
|
return { serverParams, pkceCodes, silentRequest };
|
}
|
}
|
|
export { SilentIframeClient };
|
//# sourceMappingURL=SilentIframeClient.mjs.map
|