/*! @azure/msal-browser v5.15.0 2026-06-23 */
'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, SilentHandlerMonitorIframeForHash, SilentHandlerInitiateAuthRequest, RemoveHiddenIframe, DeserializeResponse, HandleResponseCode, HandleResponseEar } from '../telemetry/BrowserPerformanceEvents.mjs';
import { createBrowserAuthError } from '../error/BrowserAuthError.mjs';
import { InteractionType, BrowserConstants } from '../utils/BrowserConstants.mjs';
import { createHiddenIframe, 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, waitForIframeResponseHook) {
        super(config, storageImpl, browserCrypto, logger, eventHandler, navigationClient, performanceClient, correlationId, platformAuthProvider);
        this.apiId = apiId;
        this.nativeStorage = nativeStorageImpl;
        this.waitForIframeResponseHook = waitForIframeResponseHook;
    }
    /**
     * 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 ${inputRequest.prompt} ${Constants.PromptValue.NONE}`, 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, undefined, this.config.system.serverTelemetryEnabled);
        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.correlationId = 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,
        };
        // Create the iframe, register the response listener, then navigate, so the listener is active before the iframe can respond.
        const iframe = createHiddenIframe();
        const responseType = this.config.auth.OIDCOptions.responseMode;
        let responseString;
        try {
            const responsePromise = invokeAsync(this.waitForIframeResponse.bind(this), SilentHandlerMonitorIframeForHash, this.logger, this.performanceClient, correlationId)(iframe, request);
            responsePromise.catch(() => {
                /*
                 * If navigation below throws before responsePromise is awaited,
                 * the listener still rejects on timeout. Swallow it here so it
                 * does not surface as an unhandled rejection; the navigation
                 * error is propagated instead.
                 */
            });
            await invokeAsync(initiateEarRequest, SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(iframe, this.config, discoveredAuthority, silentRequest, this.logger, this.performanceClient);
            responseString = await responsePromise;
        }
        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, undefined, this.config.system.serverTelemetryEnabled),
                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, undefined, this.config.system.serverTelemetryEnabled),
            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, correlationId);
        // 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,
        };
        // Create the iframe, register the response listener, then navigate, so the listener is active before the iframe can respond.
        const iframe = createHiddenIframe();
        const responseType = this.config.auth.OIDCOptions.responseMode;
        // Wait for response from the redirect bridge.
        let responseString;
        try {
            const responsePromise = invokeAsync(this.waitForIframeResponse.bind(this), SilentHandlerMonitorIframeForHash, this.logger, this.performanceClient, correlationId)(iframe, request);
            responsePromise.catch(() => {
                /*
                 * If URL creation or navigation below throws before
                 * responsePromise is awaited, the listener still rejects on
                 * timeout. Swallow it here so it does not surface as an
                 * unhandled rejection; the navigation error is propagated
                 * instead.
                 */
            });
            if (request.httpMethod === Constants.HttpMethod.POST) {
                await invokeAsync(initiateCodeFlowWithPost, SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(iframe, 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);
                // Navigate the iframe to the authorize request url
                await invokeAsync(initiateCodeRequest, SilentHandlerInitiateAuthRequest, this.logger, this.performanceClient, correlationId)(iframe, navigateUrl, this.logger, correlationId);
            }
            responseString = await responsePromise;
        }
        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 };
    }
    async waitForIframeResponse(iframe, request) {
        if (this.waitForIframeResponseHook) {
            return this.waitForIframeResponseHook(iframe, request);
        }
        return waitForBridgeResponse(this.config.system.iframeBridgeTimeout, this.logger, request, this.performanceClient, this.config.experimental);
    }
}

export { SilentIframeClient };
//# sourceMappingURL=SilentIframeClient.mjs.map
