/*! @azure/msal-browser v5.17.0 2026-07-07 */
'use strict';
import { ProtocolUtils } from '@azure/msal-common/browser';
import { base64Decode } from '../encode/Base64Decode.mjs';
import { createBrowserAuthError } from '../error/BrowserAuthError.mjs';
import { popupRelayUnsupportedFlow, timedOut, userCancelled, redirectBridgeEmptyResponse } from '../error/BrowserAuthErrorCodes.mjs';
import { POPUP_RELAY_RESPONSE_TYPE } from './constants.mjs';

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
/**
 * Builds the URL of the popup-relay page to open. The action the relay page
 * must perform (GET navigation or POST form) is carried in the page's hash
 * (client-side only, never sent to a server), keyed by the per-request
 * library-state `id` so the relay page can listen on the right BroadcastChannel
 * and echo the id back. The relay URI is resolved against the app origin, so it
 * must be same-origin as the embedded frame. See `runPopupRelay`.
 *
 * @internal
 */
function buildPopupRelayUrl(popupRelayUri, id, action, correlationId) {
    const req = { id, ...action };
    const target = new URL(popupRelayUri, window.location.origin);
    /*
     * The relay page must be same-origin as the embedded frame: the response is
     * relayed back via postMessage and only accepted from this origin, and a
     * cross-origin page would also receive the relayed /authorize request. Fail
     * fast with a clear error instead of silently relaying to (and timing out
     * against) the wrong origin.
     */
    if (target.origin !== window.location.origin) {
        throw createBrowserAuthError(popupRelayUnsupportedFlow, correlationId, "popup_relay_cross_origin");
    }
    const hashParams = new URLSearchParams();
    hashParams.set("req", JSON.stringify(req));
    target.hash = hashParams.toString();
    return target.toString();
}
/**
 * Waits for the popup-relay page to relay the raw auth response back to the
 * embedded frame via postMessage. Used in place of `waitForBridgeResponse`
 * when `auth.popupRelayUri` is configured, because third-party storage
 * partitioning blocks the BroadcastChannel the redirect bridge uses from
 * reaching the embedded frame.
 *
 * The response is accepted only from the popup window we opened, on our own
 * origin (the relay page is same-origin as the embedded frame), and only when
 * it carries the matching per-request library-state id.
 *
 * @internal
 */
async function waitForPopupRelayResponse(timeoutMs, logger, request, popupWindow, performanceClient) {
    return new Promise((resolve, reject) => {
        const correlationId = request.correlationId;
        logger.verbose("1d05g9", correlationId);
        performanceClient.addFields({ redirectBridgeTimeoutMs: timeoutMs }, correlationId);
        const { libraryState } = ProtocolUtils.parseRequestState(base64Decode, request.state || "", correlationId);
        const expectedId = libraryState.id;
        const expectedOrigin = window.location.origin;
        let settled = false;
        const cleanup = () => {
            window.removeEventListener("message", onMessage);
            clearTimeout(timeoutId);
            clearInterval(closedPoll);
        };
        const onMessage = (event) => {
            if (event.origin !== expectedOrigin ||
                event.source !== popupWindow) {
                return;
            }
            const data = event.data;
            if (!data ||
                data.type !== POPUP_RELAY_RESPONSE_TYPE ||
                data.id !== expectedId) {
                return;
            }
            if (settled) {
                return;
            }
            settled = true;
            cleanup();
            if (data.error) {
                reject(createBrowserAuthError(data.error, correlationId));
            }
            else if (data.payload) {
                resolve(data.payload);
            }
            else {
                reject(createBrowserAuthError(redirectBridgeEmptyResponse, correlationId));
            }
        };
        window.addEventListener("message", onMessage);
        const timeoutId = window.setTimeout(() => {
            if (settled) {
                return;
            }
            settled = true;
            cleanup();
            reject(createBrowserAuthError(timedOut, correlationId, "popup_relay_timeout"));
        }, timeoutMs);
        const closedPoll = window.setInterval(() => {
            if (popupWindow.closed && !settled) {
                settled = true;
                cleanup();
                reject(createBrowserAuthError(userCancelled, correlationId));
            }
        }, 500);
    });
}

export { buildPopupRelayUrl, waitForPopupRelayResponse };
//# sourceMappingURL=relayClient.mjs.map
