/*! @azure/msal-browser v5.6.0 2026-03-18 */
'use strict';
import { createClientConfigurationError, ClientConfigurationErrorCodes, invokeAsync, RequestParameterBuilder, Constants, AuthorizeProtocol, ThrottlingUtils, ResponseHandler, PerformanceEvents, TimeUtils, ProtocolMode, PopTokenGenerator, ProtocolUtils } from '@azure/msal-common/browser';
import { GetStandardParams, HandleResponsePlatformBroker, HandleCodeResponse, DecryptEarResponse, NativeInteractionClientAcquireToken } from '../telemetry/BrowserPerformanceEvents.mjs';
import { BrowserConstants } from '../utils/BrowserConstants.mjs';
import { version } from '../packageMetadata.mjs';
import { CryptoOps } from '../crypto/CryptoOps.mjs';
import { createBrowserAuthError } from '../error/BrowserAuthError.mjs';
import { InteractionHandler } from '../interaction_handler/InteractionHandler.mjs';
import { PlatformAuthInteractionClient } from '../interaction_client/PlatformAuthInteractionClient.mjs';
import { decryptEarResponse } from '../crypto/BrowserCrypto.mjs';
import { earJwkEmpty, earJweEmpty, nativeConnectionNotEstablished } from '../error/BrowserAuthErrorCodes.mjs';

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
const clientDataAccountTypeMapping = new Map([
    ["e", "AAD"],
    ["m", "MSA"],
]);
/**
 * Parses the clientdata response parameter from the /authorize endpoint.
 *
 * Logically, the clientdata value is URL-encoded and pipe-delimited:
 *   urlencoded(account_type | error | sub_error | cloud_instance | caller_data_boundary)
 *
 * In normal browser flows, this value may already have been URL-decoded
 * (e.g. by URLSearchParams). This function will only apply decodeURIComponent
 * when the string appears to contain percent-encoded sequences to avoid
 * double-decoding.
 *
 * @param clientdata - The raw clientdata string from the authorize response
 * @returns Parsed ClientData object, or null if the input is empty/invalid
 */
function parseClientData(clientdata) {
    if (!clientdata) {
        return null;
    }
    try {
        // Only decode when the string appears to contain percent-encoded sequences
        const shouldDecode = /%(?:[0-9A-Fa-f]{2})/.test(clientdata);
        const decoded = shouldDecode
            ? decodeURIComponent(clientdata)
            : clientdata;
        const parts = decoded.split("|");
        if (parts.length < 5) {
            return null;
        }
        return {
            accountType: clientDataAccountTypeMapping.get(parts[0]?.trim() || "") || "",
            error: parts[1]?.trim() || "",
            subError: parts[2]?.trim() || "",
            cloudInstance: parts[3]?.trim() || "",
            callerDataBoundary: parts[4]?.trim() || "",
        };
    }
    catch {
        return null;
    }
}
/**
 * Instruments account type, error, and suberror from clientdata
 */
function instrumentClientData(response, correlationId, performanceClient) {
    const parsed = parseClientData(response.clientdata);
    parsed?.accountType &&
        performanceClient.addFields({ accountType: parsed.accountType }, correlationId);
    parsed?.error &&
        performanceClient.addFields({ serverErrorNo: parsed.error }, correlationId);
    parsed?.subError &&
        performanceClient.addFields({ serverSubErrorNo: parsed.subError }, correlationId);
}
/**
 * Returns map of parameters that are applicable to all calls to /authorize whether using PKCE or EAR
 * @param config
 * @param authority
 * @param request
 * @param logger
 * @param performanceClient
 * @returns
 */
async function getStandardParameters(config, authority, request, logger, performanceClient) {
    const parameters = AuthorizeProtocol.getStandardAuthorizeRequestParameters({ ...config.auth, authority: authority }, request, logger, performanceClient);
    RequestParameterBuilder.addLibraryInfo(parameters, {
        sku: BrowserConstants.MSAL_SKU,
        version: version,
        os: "",
        cpu: "",
    });
    if (config.system.protocolMode !== ProtocolMode.OIDC) {
        RequestParameterBuilder.addApplicationTelemetry(parameters, config.telemetry.application);
    }
    if (request.platformBroker) {
        // signal ests that this is a WAM call
        RequestParameterBuilder.addNativeBroker(parameters);
        // pass the req_cnf for POP
        if (request.authenticationScheme === Constants.AuthenticationScheme.POP) {
            const cryptoOps = new CryptoOps(logger, performanceClient);
            const popTokenGenerator = new PopTokenGenerator(cryptoOps, performanceClient);
            // req_cnf is always sent as a string for SPAs
            let reqCnfData;
            if (!request.popKid) {
                const generatedReqCnfData = await invokeAsync(popTokenGenerator.generateCnf.bind(popTokenGenerator), PerformanceEvents.PopTokenGenerateCnf, logger, performanceClient, request.correlationId)(request, logger);
                reqCnfData = generatedReqCnfData.reqCnfString;
            }
            else {
                reqCnfData = cryptoOps.encodeKid(request.popKid);
            }
            RequestParameterBuilder.addPopToken(parameters, reqCnfData);
        }
    }
    RequestParameterBuilder.instrumentBrokerParams(parameters, request.correlationId, performanceClient);
    return parameters;
}
/**
 * Gets the full /authorize URL with request parameters when using Auth Code + PKCE
 * @param config
 * @param authority
 * @param request
 * @param logger
 * @param performanceClient
 * @returns
 */
async function getAuthCodeRequestUrl(config, authority, request, logger, performanceClient) {
    if (!request.codeChallenge) {
        throw createClientConfigurationError(ClientConfigurationErrorCodes.pkceParamsMissing);
    }
    const parameters = await invokeAsync(getStandardParameters, GetStandardParams, logger, performanceClient, request.correlationId)(config, authority, request, logger, performanceClient);
    RequestParameterBuilder.addResponseType(parameters, Constants.OAuthResponseType.CODE);
    RequestParameterBuilder.addCodeChallengeParams(parameters, request.codeChallenge, Constants.S256_CODE_CHALLENGE_METHOD);
    // Merge extraQueryParameters and extraParameters to be appended to request URL
    RequestParameterBuilder.addExtraParameters(parameters, {
        ...request.extraQueryParameters,
        ...request.extraParameters,
    });
    return AuthorizeProtocol.getAuthorizeUrl(authority, parameters);
}
/**
 * Gets the form that will be posted to /authorize with request parameters when using EAR
 */
async function getEARForm(frame, config, authority, request, logger, performanceClient) {
    if (!request.earJwk) {
        throw createBrowserAuthError(earJwkEmpty);
    }
    const parameters = await getStandardParameters(config, authority, request, logger, performanceClient);
    RequestParameterBuilder.addResponseType(parameters, Constants.OAuthResponseType.IDTOKEN_TOKEN_REFRESHTOKEN);
    RequestParameterBuilder.addEARParameters(parameters, request.earJwk);
    // Also add codeChallenge as backup in case EAR is not supported
    RequestParameterBuilder.addCodeChallengeParams(parameters, request.codeChallenge, Constants.S256_CODE_CHALLENGE_METHOD);
    RequestParameterBuilder.addExtraParameters(parameters, {
        ...request.extraParameters,
    });
    const queryParams = new Map();
    RequestParameterBuilder.addExtraParameters(queryParams, request.extraQueryParameters || {});
    // Add correlationId to query params so gateway can propagate it to IDPs
    RequestParameterBuilder.addCorrelationId(queryParams, request.correlationId);
    const url = AuthorizeProtocol.getAuthorizeUrl(authority, queryParams);
    return createForm(frame, url, parameters);
}
/**
 * Gets the form that will be posted to /authorize with request parameters when using POST method
 */
async function getCodeForm(frame, config, authority, request, logger, performanceClient) {
    const parameters = await getStandardParameters(config, authority, request, logger, performanceClient);
    RequestParameterBuilder.addResponseType(parameters, Constants.OAuthResponseType.CODE);
    RequestParameterBuilder.addCodeChallengeParams(parameters, request.codeChallenge, request.codeChallengeMethod || Constants.S256_CODE_CHALLENGE_METHOD);
    // Add extraParameters to the request body
    RequestParameterBuilder.addExtraParameters(parameters, {
        ...request.extraParameters,
    });
    // Add extraQueryParameters to be appended to request URL
    const queryParams = new Map();
    RequestParameterBuilder.addExtraParameters(queryParams, request.extraQueryParameters || {});
    // Add correlationId to query params so gateway can propagate it to IDPs
    RequestParameterBuilder.addCorrelationId(queryParams, request.correlationId);
    const url = AuthorizeProtocol.getAuthorizeUrl(authority, queryParams);
    return createForm(frame, url, parameters);
}
/**
 * Creates form element in the provided document with auth parameters in the post body
 * @param frame
 * @param authorizeUrl
 * @param parameters
 * @returns
 */
function createForm(frame, authorizeUrl, parameters) {
    const form = frame.createElement("form");
    form.method = "post";
    form.action = authorizeUrl;
    parameters.forEach((value, key) => {
        const param = frame.createElement("input");
        param.hidden = true;
        param.name = key;
        param.value = value;
        form.appendChild(param);
    });
    frame.body.appendChild(form);
    return form;
}
/**
 * Response handler when server returns accountId on the /authorize request
 * @param request
 * @param accountId
 * @param apiId
 * @param config
 * @param browserStorage
 * @param nativeStorage
 * @param eventHandler
 * @param logger
 * @param performanceClient
 * @param nativeMessageHandler
 * @returns
 */
async function handleResponsePlatformBroker(request, accountId, apiId, config, browserStorage, nativeStorage, eventHandler, logger, performanceClient, platformAuthProvider) {
    logger.verbose("11qcow", request.correlationId);
    if (!platformAuthProvider) {
        throw createBrowserAuthError(nativeConnectionNotEstablished);
    }
    const browserCrypto = new CryptoOps(logger, performanceClient);
    const nativeInteractionClient = new PlatformAuthInteractionClient(config, browserStorage, browserCrypto, logger, eventHandler, config.system.navigationClient, apiId, performanceClient, platformAuthProvider, accountId, nativeStorage, request.correlationId);
    const { userRequestState } = ProtocolUtils.parseRequestState(browserCrypto.base64Decode, request.state);
    return invokeAsync(nativeInteractionClient.acquireToken.bind(nativeInteractionClient), NativeInteractionClientAcquireToken, logger, performanceClient, request.correlationId)({
        ...request,
        state: userRequestState,
        prompt: undefined, // Server should handle the prompt, ideally native broker can do this part silently
    });
}
/**
 * Response handler when server returns code on the /authorize request
 * @param request
 * @param response
 * @param codeVerifier
 * @param authClient
 * @param browserStorage
 * @param logger
 * @param performanceClient
 * @returns
 */
async function handleResponseCode(request, response, codeVerifier, apiId, config, authClient, browserStorage, nativeStorage, eventHandler, logger, performanceClient, platformAuthProvider) {
    // Remove throttle if it exists
    ThrottlingUtils.removeThrottle(browserStorage, config.auth.clientId, request);
    // Instrument clientdata telemetry fields from the authorize response
    instrumentClientData(response, request.correlationId, performanceClient);
    if (response.accountId) {
        return invokeAsync(handleResponsePlatformBroker, HandleResponsePlatformBroker, logger, performanceClient, request.correlationId)(request, response.accountId, apiId, config, browserStorage, nativeStorage, eventHandler, logger, performanceClient, platformAuthProvider);
    }
    const authCodeRequest = {
        ...request,
        code: response.code || "",
        codeVerifier: codeVerifier,
    };
    // Create popup interaction handler.
    const interactionHandler = new InteractionHandler(authClient, browserStorage, authCodeRequest, logger, performanceClient);
    // Handle response from hash string.
    const result = await invokeAsync(interactionHandler.handleCodeResponse.bind(interactionHandler), HandleCodeResponse, logger, performanceClient, request.correlationId)(response, request, apiId);
    return result;
}
/**
 * Response handler when server returns ear_jwe on the /authorize request
 * @param request
 * @param response
 * @param apiId
 * @param config
 * @param authority
 * @param browserStorage
 * @param nativeStorage
 * @param eventHandler
 * @param logger
 * @param performanceClient
 * @param nativeMessageHandler
 * @returns
 */
async function handleResponseEAR(request, response, apiId, config, authority, browserStorage, nativeStorage, eventHandler, logger, performanceClient, platformAuthProvider) {
    // Remove throttle if it exists
    ThrottlingUtils.removeThrottle(browserStorage, config.auth.clientId, request);
    // Instrument clientdata telemetry fields from the authorize response
    instrumentClientData(response, request.correlationId, performanceClient);
    // Validate state & check response for errors
    AuthorizeProtocol.validateAuthorizationResponse(response, request.state);
    if (!response.ear_jwe) {
        throw createBrowserAuthError(earJweEmpty);
    }
    if (!request.earJwk) {
        throw createBrowserAuthError(earJwkEmpty);
    }
    const decryptedData = JSON.parse(await invokeAsync(decryptEarResponse, DecryptEarResponse, logger, performanceClient, request.correlationId)(request.earJwk, response.ear_jwe));
    if (decryptedData.accountId) {
        return invokeAsync(handleResponsePlatformBroker, HandleResponsePlatformBroker, logger, performanceClient, request.correlationId)(request, decryptedData.accountId, apiId, config, browserStorage, nativeStorage, eventHandler, logger, performanceClient, platformAuthProvider);
    }
    const responseHandler = new ResponseHandler(config.auth.clientId, browserStorage, new CryptoOps(logger, performanceClient), logger, performanceClient, null, null);
    // Validate response. This function throws a server error if an error is returned by the server.
    responseHandler.validateTokenResponse(decryptedData, request.correlationId);
    // Temporary until response handler is refactored to be more flow agnostic.
    const additionalData = {
        code: "",
        state: request.state,
        nonce: request.nonce,
        client_info: decryptedData.client_info,
        cloud_graph_host_name: decryptedData.cloud_graph_host_name,
        cloud_instance_host_name: decryptedData.cloud_instance_host_name,
        cloud_instance_name: decryptedData.cloud_instance_name,
        msgraph_host: decryptedData.msgraph_host,
    };
    return (await invokeAsync(responseHandler.handleServerTokenResponse.bind(responseHandler), PerformanceEvents.HandleServerTokenResponse, logger, performanceClient, request.correlationId)(decryptedData, authority, TimeUtils.nowSeconds(), request, apiId, additionalData, undefined, undefined, undefined, undefined));
}

export { getAuthCodeRequestUrl, getCodeForm, getEARForm, handleResponseCode, handleResponseEAR, handleResponsePlatformBroker, parseClientData };
//# sourceMappingURL=Authorize.mjs.map
