import * as oauth from 'oauth4webapi';
import { compactDecrypt } from 'jose/jwe/compact/decrypt';
import { JOSEError } from 'jose/errors';
let headers;
let USER_AGENT;
if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
    const NAME = 'openid-client';
    const VERSION = 'v6.8.2';
    USER_AGENT = `${NAME}/${VERSION}`;
    headers = { 'user-agent': USER_AGENT };
}
const int = (config) => {
    return props.get(config);
};
let props;
export { AuthorizationResponseError, ResponseBodyError, WWWAuthenticateChallengeError, } from 'oauth4webapi';
let tbi;
export function ClientSecretPost(clientSecret) {
    if (clientSecret !== undefined) {
        return oauth.ClientSecretPost(clientSecret);
    }
    tbi ||= new WeakMap();
    return (as, client, body, headers) => {
        let auth;
        if (!(auth = tbi.get(client))) {
            assertString(client.client_secret, '"metadata.client_secret"');
            auth = oauth.ClientSecretPost(client.client_secret);
            tbi.set(client, auth);
        }
        return auth(as, client, body, headers);
    };
}
function assertString(input, it) {
    if (typeof input !== 'string') {
        throw CodedTypeError(`${it} must be a string`, ERR_INVALID_ARG_TYPE);
    }
    if (input.length === 0) {
        throw CodedTypeError(`${it} must not be empty`, ERR_INVALID_ARG_VALUE);
    }
}
export function ClientSecretBasic(clientSecret) {
    if (clientSecret !== undefined) {
        return oauth.ClientSecretBasic(clientSecret);
    }
    tbi ||= new WeakMap();
    return (as, client, body, headers) => {
        let auth;
        if (!(auth = tbi.get(client))) {
            assertString(client.client_secret, '"metadata.client_secret"');
            auth = oauth.ClientSecretBasic(client.client_secret);
            tbi.set(client, auth);
        }
        return auth(as, client, body, headers);
    };
}
export function ClientSecretJwt(clientSecret, options) {
    if (clientSecret !== undefined) {
        return oauth.ClientSecretJwt(clientSecret, options);
    }
    tbi ||= new WeakMap();
    return (as, client, body, headers) => {
        let auth;
        if (!(auth = tbi.get(client))) {
            assertString(client.client_secret, '"metadata.client_secret"');
            auth = oauth.ClientSecretJwt(client.client_secret, options);
            tbi.set(client, auth);
        }
        return auth(as, client, body, headers);
    };
}
export function None() {
    return oauth.None();
}
export function PrivateKeyJwt(clientPrivateKey, options) {
    return oauth.PrivateKeyJwt(clientPrivateKey, options);
}
export function TlsClientAuth() {
    return oauth.TlsClientAuth();
}
export const skipStateCheck = oauth.skipStateCheck;
export const skipSubjectCheck = oauth.skipSubjectCheck;
export const customFetch = oauth.customFetch;
export const modifyAssertion = oauth.modifyAssertion;
export const clockSkew = oauth.clockSkew;
export const clockTolerance = oauth.clockTolerance;
const ERR_INVALID_ARG_VALUE = 'ERR_INVALID_ARG_VALUE';
const ERR_INVALID_ARG_TYPE = 'ERR_INVALID_ARG_TYPE';
function CodedTypeError(message, code, cause) {
    const err = new TypeError(message, { cause });
    Object.assign(err, { code });
    return err;
}
export function calculatePKCECodeChallenge(codeVerifier) {
    return oauth.calculatePKCECodeChallenge(codeVerifier);
}
export function randomPKCECodeVerifier() {
    return oauth.generateRandomCodeVerifier();
}
export function randomNonce() {
    return oauth.generateRandomNonce();
}
export function randomState() {
    return oauth.generateRandomState();
}
export class ClientError extends Error {
    code;
    constructor(message, options) {
        super(message, options);
        this.name = this.constructor.name;
        this.code = options?.code;
        Error.captureStackTrace?.(this, this.constructor);
    }
}
const decoder = new TextDecoder();
function e(msg, cause, code) {
    return new ClientError(msg, { cause, code });
}
function errorHandler(err) {
    if (err instanceof TypeError ||
        err instanceof ClientError ||
        err instanceof oauth.ResponseBodyError ||
        err instanceof oauth.AuthorizationResponseError ||
        err instanceof oauth.WWWAuthenticateChallengeError) {
        throw err;
    }
    if (err instanceof oauth.OperationProcessingError) {
        switch (err.code) {
            case oauth.HTTP_REQUEST_FORBIDDEN:
                throw e('only requests to HTTPS are allowed', err, err.code);
            case oauth.REQUEST_PROTOCOL_FORBIDDEN:
                throw e('only requests to HTTP or HTTPS are allowed', err, err.code);
            case oauth.RESPONSE_IS_NOT_CONFORM:
                throw e('unexpected HTTP response status code', err.cause, err.code);
            case oauth.RESPONSE_IS_NOT_JSON:
                throw e('unexpected response content-type', err.cause, err.code);
            case oauth.PARSE_ERROR:
                throw e('parsing error occured', err, err.code);
            case oauth.INVALID_RESPONSE:
                throw e('invalid response encountered', err, err.code);
            case oauth.JWT_CLAIM_COMPARISON:
                throw e('unexpected JWT claim value encountered', err, err.code);
            case oauth.JSON_ATTRIBUTE_COMPARISON:
                throw e('unexpected JSON attribute value encountered', err, err.code);
            case oauth.JWT_TIMESTAMP_CHECK:
                throw e('JWT timestamp claim value failed validation', err, err.code);
            default:
                throw e(err.message, err, err.code);
        }
    }
    if (err instanceof oauth.UnsupportedOperationError) {
        throw e('unsupported operation', err, err.code);
    }
    if (err instanceof DOMException) {
        switch (err.name) {
            case 'OperationError':
                throw e('runtime operation error', err, oauth.UNSUPPORTED_OPERATION);
            case 'NotSupportedError':
                throw e('runtime unsupported operation', err, oauth.UNSUPPORTED_OPERATION);
            case 'TimeoutError':
                throw e('operation timed out', err, 'OAUTH_TIMEOUT');
            case 'AbortError':
                throw e('operation aborted', err, 'OAUTH_ABORT');
        }
    }
    throw new ClientError('something went wrong', { cause: err });
}
export function randomDPoPKeyPair(alg, options) {
    return oauth
        .generateKeyPair(alg ?? 'ES256', {
        extractable: options?.extractable,
    })
        .catch(errorHandler);
}
function handleEntraId(server, as, options) {
    if (server.origin === 'https://login.microsoftonline.com' &&
        (!options?.algorithm || options.algorithm === 'oidc')) {
        as[kEntraId] = true;
        return true;
    }
    return false;
}
function handleB2Clogin(server, options) {
    if (server.hostname.endsWith('.b2clogin.com') &&
        (!options?.algorithm || options.algorithm === 'oidc')) {
        return true;
    }
    return false;
}
export async function dynamicClientRegistration(server, metadata, clientAuthentication, options) {
    let as;
    if (options?.flag === retry) {
        as = options.as;
    }
    else {
        as = await performDiscovery(server, options);
    }
    const clockSkew = metadata[oauth.clockSkew] ?? 0;
    const clockTolerance = metadata[oauth.clockTolerance] ?? 30;
    metadata = structuredClone(metadata);
    const timeout = options?.timeout ?? 30;
    const signal = AbortSignal.timeout(timeout * 1000);
    let registered;
    try {
        registered = await oauth
            .dynamicClientRegistrationRequest(as, metadata, {
            initialAccessToken: options?.initialAccessToken,
            DPoP: options?.DPoP,
            headers: new Headers(headers),
            [oauth.customFetch]: options?.[customFetch],
            [oauth.allowInsecureRequests]: options?.execute?.includes(allowInsecureRequests),
            signal,
        })
            .then(oauth.processDynamicClientRegistrationResponse);
    }
    catch (err) {
        if (retryable(err, options)) {
            return dynamicClientRegistration(server, metadata, clientAuthentication, {
                ...options,
                flag: retry,
                as,
            });
        }
        errorHandler(err);
    }
    registered[oauth.clockSkew] = clockSkew;
    registered[oauth.clockTolerance] = clockTolerance;
    const instance = new Configuration(as, registered.client_id, registered, clientAuthentication);
    let internals = int(instance);
    if (options?.[customFetch]) {
        internals.fetch = options[customFetch];
    }
    if (options?.timeout) {
        internals.timeout = options.timeout;
    }
    if (options?.execute) {
        for (const extension of options.execute) {
            extension(instance);
        }
    }
    return instance;
}
export async function discovery(server, clientId, metadata, clientAuthentication, options) {
    const as = await performDiscovery(server, options);
    const instance = new Configuration(as, clientId, metadata, clientAuthentication);
    let internals = int(instance);
    if (options?.[customFetch]) {
        internals.fetch = options[customFetch];
    }
    if (options?.timeout) {
        internals.timeout = options.timeout;
    }
    if (options?.execute) {
        for (const extension of options.execute) {
            extension(instance);
        }
    }
    return instance;
}
async function performDiscovery(server, options) {
    if (!(server instanceof URL)) {
        throw CodedTypeError('"server" must be an instance of URL', ERR_INVALID_ARG_TYPE);
    }
    const resolve = !server.href.includes('/.well-known/');
    const timeout = options?.timeout ?? 30;
    const signal = AbortSignal.timeout(timeout * 1000);
    const as = await (resolve
        ? oauth.discoveryRequest(server, {
            algorithm: options?.algorithm,
            [oauth.customFetch]: options?.[customFetch],
            [oauth.allowInsecureRequests]: options?.execute?.includes(allowInsecureRequests),
            signal,
            headers: new Headers(headers),
        })
        : (options?.[customFetch] || fetch)((() => {
            oauth.checkProtocol(server, options?.execute?.includes(allowInsecureRequests) ? false : true);
            return server.href;
        })(), {
            headers: Object.fromEntries(new Headers({ accept: 'application/json', ...headers }).entries()),
            body: undefined,
            method: 'GET',
            redirect: 'manual',
            signal,
        }))
        .then((response) => oauth.processDiscoveryResponse(oauth._nodiscoverycheck, response))
        .catch(errorHandler);
    if (resolve && new URL(as.issuer).href !== server.href) {
        handleEntraId(server, as, options) ||
            handleB2Clogin(server, options) ||
            (() => {
                throw new ClientError('discovered metadata issuer does not match the expected issuer', {
                    code: oauth.JSON_ATTRIBUTE_COMPARISON,
                    cause: {
                        expected: server.href,
                        body: as,
                        attribute: 'issuer',
                    },
                });
            })();
    }
    return as;
}
function isRsaOaep(input) {
    return input.name === 'RSA-OAEP';
}
function isEcdh(input) {
    return input.name === 'ECDH';
}
const ecdhEs = 'ECDH-ES';
const ecdhEsA128Kw = 'ECDH-ES+A128KW';
const ecdhEsA192Kw = 'ECDH-ES+A192KW';
const ecdhEsA256Kw = 'ECDH-ES+A256KW';
function checkEcdhAlg(algs, alg, pk) {
    switch (alg) {
        case undefined:
            algs.add(ecdhEs);
            algs.add(ecdhEsA128Kw);
            algs.add(ecdhEsA192Kw);
            algs.add(ecdhEsA256Kw);
            break;
        case ecdhEs:
        case ecdhEsA128Kw:
        case ecdhEsA192Kw:
        case ecdhEsA256Kw:
            algs.add(alg);
            break;
        default:
            throw CodedTypeError('invalid key alg', ERR_INVALID_ARG_VALUE, { pk });
    }
}
export function enableDecryptingResponses(config, contentEncryptionAlgorithms = [
    'A128GCM',
    'A192GCM',
    'A256GCM',
    'A128CBC-HS256',
    'A192CBC-HS384',
    'A256CBC-HS512',
], ...keys) {
    if (int(config).decrypt !== undefined) {
        throw new TypeError('enableDecryptingResponses can only be called on a given Configuration instance once');
    }
    if (keys.length === 0) {
        throw CodedTypeError('no keys were provided', ERR_INVALID_ARG_VALUE);
    }
    const algs = new Set();
    const normalized = [];
    for (const pk of keys) {
        let key;
        if ('key' in pk) {
            key = { key: pk.key };
            if (typeof pk.alg === 'string')
                key.alg = pk.alg;
            if (typeof pk.kid === 'string')
                key.kid = pk.kid;
        }
        else {
            key = { key: pk };
        }
        if (key.key.type !== 'private') {
            throw CodedTypeError('only private keys must be provided', ERR_INVALID_ARG_VALUE);
        }
        if (isRsaOaep(key.key.algorithm)) {
            switch (key.key.algorithm.hash.name) {
                case 'SHA-1':
                case 'SHA-256':
                case 'SHA-384':
                case 'SHA-512': {
                    let alg = 'RSA-OAEP';
                    let sha;
                    if ((sha = parseInt(key.key.algorithm.hash.name.slice(-3), 10))) {
                        alg = `${alg}-${sha}`;
                    }
                    key.alg ||= alg;
                    if (alg !== key.alg)
                        throw CodedTypeError('invalid key alg', ERR_INVALID_ARG_VALUE, {
                            pk,
                        });
                    algs.add(key.alg);
                    break;
                }
                default:
                    throw CodedTypeError('only SHA-512, SHA-384, SHA-256, and SHA-1 RSA-OAEP keys are supported', ERR_INVALID_ARG_VALUE);
            }
        }
        else if (isEcdh(key.key.algorithm)) {
            if (key.key.algorithm.namedCurve !== 'P-256') {
                throw CodedTypeError('Only P-256 ECDH keys are supported', ERR_INVALID_ARG_VALUE);
            }
            checkEcdhAlg(algs, key.alg, pk);
        }
        else if (key.key.algorithm.name === 'X25519') {
            checkEcdhAlg(algs, key.alg, pk);
        }
        else {
            throw CodedTypeError('only RSA-OAEP, ECDH, or X25519 keys are supported', ERR_INVALID_ARG_VALUE);
        }
        normalized.push(key);
    }
    int(config).decrypt = async (jwe) => decrypt(normalized, jwe, contentEncryptionAlgorithms, [...algs]).catch(errorHandler);
}
function checkCryptoKey(key, alg, epk) {
    if (alg.startsWith('RSA-OAEP')) {
        return true;
    }
    if (alg.startsWith('ECDH-ES')) {
        if (key.algorithm.name !== 'ECDH' && key.algorithm.name !== 'X25519') {
            return false;
        }
        if (key.algorithm.name === 'ECDH') {
            return epk?.crv === key.algorithm.namedCurve;
        }
        if (key.algorithm.name === 'X25519') {
            return epk?.crv === 'X25519';
        }
    }
    return false;
}
function selectCryptoKeyForDecryption(keys, alg, kid, epk) {
    const { 0: key, length } = keys.filter((key) => {
        if (kid !== key.kid) {
            return false;
        }
        if (key.alg && alg !== key.alg) {
            return false;
        }
        return checkCryptoKey(key.key, alg, epk);
    });
    if (!key) {
        throw e('no applicable decryption key selected', undefined, 'OAUTH_DECRYPTION_FAILED');
    }
    if (length !== 1) {
        throw e('multiple applicable decryption keys selected', undefined, 'OAUTH_DECRYPTION_FAILED');
    }
    return key.key;
}
async function decrypt(keys, jwe, contentEncryptionAlgorithms, keyManagementAlgorithms) {
    return decoder.decode((await compactDecrypt(jwe, (header) => {
        const { kid, alg, epk } = header;
        return selectCryptoKeyForDecryption(keys, alg, kid, epk);
    }, { keyManagementAlgorithms, contentEncryptionAlgorithms }).catch((err) => {
        if (err instanceof JOSEError) {
            throw e('decryption failed', err, 'OAUTH_DECRYPTION_FAILED');
        }
        errorHandler(err);
    })).plaintext);
}
function getServerHelpers(metadata) {
    return {
        supportsPKCE: {
            __proto__: null,
            value(method = 'S256') {
                return (metadata.code_challenge_methods_supported?.includes(method) === true);
            },
        },
    };
}
function addServerHelpers(metadata) {
    Object.defineProperties(metadata, getServerHelpers(metadata));
}
const kEntraId = Symbol();
export class Configuration {
    constructor(server, clientId, metadata, clientAuthentication) {
        if (typeof clientId !== 'string' || !clientId.length) {
            throw CodedTypeError('"clientId" must be a non-empty string', ERR_INVALID_ARG_TYPE);
        }
        if (typeof metadata === 'string') {
            metadata = { client_secret: metadata };
        }
        if (metadata?.client_id !== undefined && clientId !== metadata.client_id) {
            throw CodedTypeError('"clientId" and "metadata.client_id" must be the same', ERR_INVALID_ARG_VALUE);
        }
        const client = {
            ...structuredClone(metadata),
            client_id: clientId,
        };
        client[oauth.clockSkew] = metadata?.[oauth.clockSkew] ?? 0;
        client[oauth.clockTolerance] = metadata?.[oauth.clockTolerance] ?? 30;
        let auth;
        if (clientAuthentication) {
            auth = clientAuthentication;
        }
        else {
            if (typeof client.client_secret === 'string' &&
                client.client_secret.length) {
                auth = ClientSecretPost(client.client_secret);
            }
            else {
                auth = None();
            }
        }
        let c = Object.freeze(client);
        const clone = structuredClone(server);
        if (kEntraId in server) {
            clone[oauth._expectedIssuer] = ({ claims: { tid } }) => server.issuer.replace('{tenantid}', tid);
        }
        let as = Object.freeze(clone);
        props ||= new WeakMap();
        props.set(this, {
            __proto__: null,
            as,
            c,
            auth,
            tlsOnly: true,
            jwksCache: {},
        });
    }
    serverMetadata() {
        const metadata = structuredClone(int(this).as);
        addServerHelpers(metadata);
        return metadata;
    }
    clientMetadata() {
        const metadata = structuredClone(int(this).c);
        return metadata;
    }
    get timeout() {
        return int(this).timeout;
    }
    set timeout(value) {
        int(this).timeout = value;
    }
    get [customFetch]() {
        return int(this).fetch;
    }
    set [customFetch](value) {
        int(this).fetch = value;
    }
}
Object.freeze(Configuration.prototype);
function getHelpers(response) {
    let exp = undefined;
    if (response.expires_in !== undefined) {
        const now = new Date();
        now.setSeconds(now.getSeconds() + response.expires_in);
        exp = now.getTime();
    }
    return {
        expiresIn: {
            __proto__: null,
            value() {
                if (exp) {
                    const now = Date.now();
                    if (exp > now) {
                        return Math.floor((exp - now) / 1000);
                    }
                    return 0;
                }
                return undefined;
            },
        },
        claims: {
            __proto__: null,
            value() {
                try {
                    return oauth.getValidatedIdTokenClaims(this);
                }
                catch {
                    return undefined;
                }
            },
        },
    };
}
function addHelpers(response) {
    Object.defineProperties(response, getHelpers(response));
}
export function getDPoPHandle(config, keyPair, options) {
    checkConfig(config);
    return oauth.DPoP(int(config).c, keyPair, options);
}
async function handleRetryAfter(response, currentInterval, signal, throwIfInvalid = false) {
    const retryAfter = response.headers.get('retry-after')?.trim();
    if (retryAfter === undefined)
        return;
    let delaySeconds;
    if (/^\d+$/.test(retryAfter)) {
        delaySeconds = parseInt(retryAfter, 10);
    }
    else {
        const retryDate = new Date(retryAfter);
        if (Number.isFinite(retryDate.getTime())) {
            const now = new Date();
            const delayMs = retryDate.getTime() - now.getTime();
            if (delayMs > 0) {
                delaySeconds = Math.ceil(delayMs / 1000);
            }
        }
    }
    if (throwIfInvalid && !Number.isFinite(delaySeconds)) {
        throw new oauth.OperationProcessingError('invalid Retry-After header value', { cause: response });
    }
    if (delaySeconds > currentInterval) {
        await wait(delaySeconds - currentInterval, signal);
    }
}
function wait(duration, signal) {
    return new Promise((resolve, reject) => {
        const waitStep = (remaining) => {
            try {
                signal.throwIfAborted();
            }
            catch (err) {
                reject(err);
                return;
            }
            if (remaining <= 0) {
                resolve();
                return;
            }
            const currentWait = Math.min(remaining, 5);
            setTimeout(() => waitStep(remaining - currentWait), currentWait * 1000);
        };
        waitStep(duration);
    });
}
export async function pollDeviceAuthorizationGrant(config, deviceAuthorizationResponse, parameters, options) {
    checkConfig(config);
    parameters = new URLSearchParams(parameters);
    let interval = deviceAuthorizationResponse.interval ?? 5;
    const pollingSignal = options?.signal ??
        AbortSignal.timeout(deviceAuthorizationResponse.expires_in * 1000);
    try {
        await wait(interval, pollingSignal);
    }
    catch (err) {
        errorHandler(err);
    }
    const { as, c, auth, fetch, tlsOnly, nonRepudiation, timeout, decrypt } = int(config);
    const retryPoll = (updatedInterval, flag) => pollDeviceAuthorizationGrant(config, {
        ...deviceAuthorizationResponse,
        interval: updatedInterval,
    }, parameters, {
        ...options,
        signal: pollingSignal,
        flag,
    });
    const response = await oauth
        .deviceCodeGrantRequest(as, c, auth, deviceAuthorizationResponse.device_code, {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        additionalParameters: parameters,
        DPoP: options?.DPoP,
        headers: new Headers(headers),
        signal: pollingSignal.aborted ? pollingSignal : signal(timeout),
    })
        .catch(errorHandler);
    if (response.status === 503 && response.headers.has('retry-after')) {
        await handleRetryAfter(response, interval, pollingSignal, true);
        await response.body?.cancel();
        return retryPoll(interval);
    }
    const p = oauth.processDeviceCodeResponse(as, c, response, {
        [oauth.jweDecrypt]: decrypt,
    });
    let result;
    try {
        result = await p;
    }
    catch (err) {
        if (retryable(err, options)) {
            return retryPoll(interval, retry);
        }
        if (err instanceof oauth.ResponseBodyError) {
            switch (err.error) {
                case 'slow_down':
                    interval += 5;
                case 'authorization_pending':
                    await handleRetryAfter(err.response, interval, pollingSignal);
                    return retryPoll(interval);
            }
        }
        errorHandler(err);
    }
    result.id_token && (await nonRepudiation?.(response));
    addHelpers(result);
    return result;
}
export async function initiateDeviceAuthorization(config, parameters) {
    checkConfig(config);
    const { as, c, auth, fetch, tlsOnly, timeout } = int(config);
    return oauth
        .deviceAuthorizationRequest(as, c, auth, parameters, {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        headers: new Headers(headers),
        signal: signal(timeout),
    })
        .then((response) => oauth.processDeviceAuthorizationResponse(as, c, response))
        .catch(errorHandler);
}
export async function initiateBackchannelAuthentication(config, parameters) {
    checkConfig(config);
    const { as, c, auth, fetch, tlsOnly, timeout } = int(config);
    return oauth
        .backchannelAuthenticationRequest(as, c, auth, parameters, {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        headers: new Headers(headers),
        signal: signal(timeout),
    })
        .then((response) => oauth.processBackchannelAuthenticationResponse(as, c, response))
        .catch(errorHandler);
}
export async function pollBackchannelAuthenticationGrant(config, backchannelAuthenticationResponse, parameters, options) {
    checkConfig(config);
    parameters = new URLSearchParams(parameters);
    let interval = backchannelAuthenticationResponse.interval ?? 5;
    const pollingSignal = options?.signal ??
        AbortSignal.timeout(backchannelAuthenticationResponse.expires_in * 1000);
    try {
        await wait(interval, pollingSignal);
    }
    catch (err) {
        errorHandler(err);
    }
    const { as, c, auth, fetch, tlsOnly, nonRepudiation, timeout, decrypt } = int(config);
    const retryPoll = (updatedInterval, flag) => pollBackchannelAuthenticationGrant(config, {
        ...backchannelAuthenticationResponse,
        interval: updatedInterval,
    }, parameters, {
        ...options,
        signal: pollingSignal,
        flag,
    });
    const response = await oauth
        .backchannelAuthenticationGrantRequest(as, c, auth, backchannelAuthenticationResponse.auth_req_id, {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        additionalParameters: parameters,
        DPoP: options?.DPoP,
        headers: new Headers(headers),
        signal: pollingSignal.aborted ? pollingSignal : signal(timeout),
    })
        .catch(errorHandler);
    if (response.status === 503 && response.headers.has('retry-after')) {
        await handleRetryAfter(response, interval, pollingSignal, true);
        await response.body?.cancel();
        return retryPoll(interval);
    }
    const p = oauth.processBackchannelAuthenticationGrantResponse(as, c, response, {
        [oauth.jweDecrypt]: decrypt,
    });
    let result;
    try {
        result = await p;
    }
    catch (err) {
        if (retryable(err, options)) {
            return retryPoll(interval, retry);
        }
        if (err instanceof oauth.ResponseBodyError) {
            switch (err.error) {
                case 'slow_down':
                    interval += 5;
                case 'authorization_pending':
                    await handleRetryAfter(err.response, interval, pollingSignal);
                    return retryPoll(interval);
            }
        }
        errorHandler(err);
    }
    result.id_token && (await nonRepudiation?.(response));
    addHelpers(result);
    return result;
}
export function allowInsecureRequests(config) {
    int(config).tlsOnly = false;
}
export function setJwksCache(config, jwksCache) {
    int(config).jwksCache = structuredClone(jwksCache);
}
export function getJwksCache(config) {
    const cache = int(config).jwksCache;
    if (cache.uat) {
        return cache;
    }
    return undefined;
}
export function enableNonRepudiationChecks(config) {
    checkConfig(config);
    int(config).nonRepudiation = (response) => {
        const { as, fetch, tlsOnly, timeout, jwksCache } = int(config);
        return oauth
            .validateApplicationLevelSignature(as, response, {
            [oauth.customFetch]: fetch,
            [oauth.allowInsecureRequests]: !tlsOnly,
            headers: new Headers(headers),
            signal: signal(timeout),
            [oauth.jwksCache]: jwksCache,
        })
            .catch(errorHandler);
    };
}
export function useJwtResponseMode(config) {
    checkConfig(config);
    const { hybrid, implicit } = int(config);
    if (hybrid || implicit) {
        throw e('JARM cannot be combined with a hybrid or implicit response types', undefined, oauth.UNSUPPORTED_OPERATION);
    }
    int(config).jarm = (authorizationResponse, expectedState) => validateJARMResponse(config, authorizationResponse, expectedState);
}
export function enableDetachedSignatureResponseChecks(config) {
    if (!int(config).hybrid) {
        throw e('"code id_token" response type must be configured to be used first', undefined, oauth.UNSUPPORTED_OPERATION);
    }
    int(config).hybrid = (authorizationResponse, expectedNonce, expectedState, maxAge) => validateCodeIdTokenResponse(config, authorizationResponse, expectedNonce, expectedState, maxAge, true);
}
export async function implicitAuthentication(config, currentUrl, expectedNonce, checks) {
    checkConfig(config);
    if (!(currentUrl instanceof URL) &&
        !webInstanceOf(currentUrl, 'Request')) {
        throw CodedTypeError('"currentUrl" must be an instance of URL, or Request', ERR_INVALID_ARG_TYPE);
    }
    if (typeof expectedNonce !== 'string') {
        throw CodedTypeError('"expectedNonce" must be a string', ERR_INVALID_ARG_TYPE);
    }
    const { as, c, fetch, tlsOnly, timeout, decrypt, implicit, jwksCache } = int(config);
    if (!implicit) {
        throw new TypeError('implicitAuthentication() cannot be used by clients using flows other than response_type=id_token');
    }
    let params;
    if (!(currentUrl instanceof URL)) {
        const request = currentUrl;
        switch (request.method) {
            case 'GET':
                params = new URLSearchParams(new URL(request.url).hash.slice(1));
                break;
            case 'POST':
                params = new URLSearchParams(await oauth.formPostResponse(request));
                break;
            default:
                throw CodedTypeError('unexpected Request HTTP method', ERR_INVALID_ARG_VALUE);
        }
    }
    else {
        params = new URLSearchParams(currentUrl.hash.slice(1));
    }
    try {
        {
            const decoy = new URLSearchParams(params);
            decoy.delete('id_token');
            oauth.validateAuthResponse({
                ...as,
                authorization_response_iss_parameter_supported: undefined,
            }, c, decoy, checks?.expectedState);
        }
        {
            const decoy = new Response(JSON.stringify({
                access_token: 'decoy',
                token_type: 'bearer',
                id_token: params.get('id_token'),
            }), {
                headers: new Headers({ 'content-type': 'application/json' }),
            });
            const ref = await oauth.processAuthorizationCodeResponse(as, c, decoy, {
                expectedNonce,
                maxAge: checks?.maxAge,
                [oauth.jweDecrypt]: decrypt,
            });
            await oauth.validateApplicationLevelSignature(as, decoy, {
                [oauth.customFetch]: fetch,
                [oauth.allowInsecureRequests]: !tlsOnly,
                headers: new Headers(headers),
                signal: signal(timeout),
                [oauth.jwksCache]: jwksCache,
            });
            return oauth.getValidatedIdTokenClaims(ref);
        }
    }
    catch (err) {
        errorHandler(err);
    }
}
export function useCodeIdTokenResponseType(config) {
    checkConfig(config);
    const { jarm, implicit } = int(config);
    if (jarm || implicit) {
        throw e('"code id_token" response type cannot be combined with JARM or implicit response type', undefined, oauth.UNSUPPORTED_OPERATION);
    }
    int(config).hybrid = (authorizationResponse, expectedNonce, expectedState, maxAge) => validateCodeIdTokenResponse(config, authorizationResponse, expectedNonce, expectedState, maxAge, false);
}
export function useIdTokenResponseType(config) {
    checkConfig(config);
    const { jarm, hybrid } = int(config);
    if (jarm || hybrid) {
        throw e('"id_token" response type cannot be combined with JARM or hybrid response type', undefined, oauth.UNSUPPORTED_OPERATION);
    }
    int(config).implicit = true;
}
function stripParams(url) {
    url = new URL(url);
    url.search = '';
    url.hash = '';
    return url.href;
}
function webInstanceOf(input, toStringTag) {
    try {
        return Object.getPrototypeOf(input)[Symbol.toStringTag] === toStringTag;
    }
    catch {
        return false;
    }
}
export async function authorizationCodeGrant(config, currentUrl, checks, tokenEndpointParameters, options) {
    checkConfig(config);
    if (options?.flag !== retry &&
        !(currentUrl instanceof URL) &&
        !webInstanceOf(currentUrl, 'Request')) {
        throw CodedTypeError('"currentUrl" must be an instance of URL, or Request', ERR_INVALID_ARG_TYPE);
    }
    let authResponse;
    let redirectUri;
    const { as, c, auth, fetch, tlsOnly, jarm, hybrid, nonRepudiation, timeout, decrypt, implicit } = int(config);
    if (options?.flag === retry) {
        authResponse = options.authResponse;
        redirectUri = options.redirectUri;
    }
    else {
        if (!(currentUrl instanceof URL)) {
            const request = currentUrl;
            currentUrl = new URL(currentUrl.url);
            switch (request.method) {
                case 'GET':
                    break;
                case 'POST':
                    const params = new URLSearchParams(await oauth.formPostResponse(request));
                    if (hybrid) {
                        currentUrl.hash = params.toString();
                    }
                    else {
                        for (const [k, v] of params.entries()) {
                            currentUrl.searchParams.append(k, v);
                        }
                    }
                    break;
                default:
                    throw CodedTypeError('unexpected Request HTTP method', ERR_INVALID_ARG_VALUE);
            }
        }
        redirectUri = stripParams(currentUrl);
        switch (true) {
            case !!jarm:
                authResponse = await jarm(currentUrl, checks?.expectedState);
                break;
            case !!hybrid:
                authResponse = await hybrid(currentUrl, checks?.expectedNonce, checks?.expectedState, checks?.maxAge);
                break;
            case !!implicit:
                throw new TypeError('authorizationCodeGrant() cannot be used by response_type=id_token clients');
            default:
                try {
                    authResponse = oauth.validateAuthResponse(as, c, currentUrl.searchParams, checks?.expectedState);
                }
                catch (err) {
                    errorHandler(err);
                }
        }
    }
    const response = await oauth
        .authorizationCodeGrantRequest(as, c, auth, authResponse, redirectUri, checks?.pkceCodeVerifier || oauth.nopkce, {
        additionalParameters: tokenEndpointParameters,
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        DPoP: options?.DPoP,
        headers: new Headers(headers),
        signal: signal(timeout),
    })
        .catch(errorHandler);
    if (typeof checks?.expectedNonce === 'string' ||
        typeof checks?.maxAge === 'number') {
        checks.idTokenExpected = true;
    }
    const p = oauth.processAuthorizationCodeResponse(as, c, response, {
        expectedNonce: checks?.expectedNonce,
        maxAge: checks?.maxAge,
        requireIdToken: checks?.idTokenExpected,
        [oauth.jweDecrypt]: decrypt,
    });
    let result;
    try {
        result = await p;
    }
    catch (err) {
        if (retryable(err, options)) {
            return authorizationCodeGrant(config, undefined, checks, tokenEndpointParameters, {
                ...options,
                flag: retry,
                authResponse: authResponse,
                redirectUri: redirectUri,
            });
        }
        errorHandler(err);
    }
    result.id_token && (await nonRepudiation?.(response));
    addHelpers(result);
    return result;
}
async function validateJARMResponse(config, authorizationResponse, expectedState) {
    const { as, c, fetch, tlsOnly, timeout, decrypt, jwksCache } = int(config);
    return oauth
        .validateJwtAuthResponse(as, c, authorizationResponse, expectedState, {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        headers: new Headers(headers),
        signal: signal(timeout),
        [oauth.jweDecrypt]: decrypt,
        [oauth.jwksCache]: jwksCache,
    })
        .catch(errorHandler);
}
async function validateCodeIdTokenResponse(config, authorizationResponse, expectedNonce, expectedState, maxAge, fapi) {
    if (typeof expectedNonce !== 'string') {
        throw CodedTypeError('"expectedNonce" must be a string', ERR_INVALID_ARG_TYPE);
    }
    if (expectedState !== undefined && typeof expectedState !== 'string') {
        throw CodedTypeError('"expectedState" must be a string', ERR_INVALID_ARG_TYPE);
    }
    const { as, c, fetch, tlsOnly, timeout, decrypt, jwksCache } = int(config);
    return (fapi
        ? oauth.validateDetachedSignatureResponse
        : oauth.validateCodeIdTokenResponse)(as, c, authorizationResponse, expectedNonce, expectedState, maxAge, {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        headers: new Headers(headers),
        signal: signal(timeout),
        [oauth.jweDecrypt]: decrypt,
        [oauth.jwksCache]: jwksCache,
    }).catch(errorHandler);
}
export async function refreshTokenGrant(config, refreshToken, parameters, options) {
    checkConfig(config);
    parameters = new URLSearchParams(parameters);
    const { as, c, auth, fetch, tlsOnly, nonRepudiation, timeout, decrypt } = int(config);
    const response = await oauth
        .refreshTokenGrantRequest(as, c, auth, refreshToken, {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        additionalParameters: parameters,
        DPoP: options?.DPoP,
        headers: new Headers(headers),
        signal: signal(timeout),
    })
        .catch(errorHandler);
    const p = oauth.processRefreshTokenResponse(as, c, response, {
        [oauth.jweDecrypt]: decrypt,
    });
    let result;
    try {
        result = await p;
    }
    catch (err) {
        if (retryable(err, options)) {
            return refreshTokenGrant(config, refreshToken, parameters, {
                ...options,
                flag: retry,
            });
        }
        errorHandler(err);
    }
    result.id_token && (await nonRepudiation?.(response));
    addHelpers(result);
    return result;
}
export async function clientCredentialsGrant(config, parameters, options) {
    checkConfig(config);
    parameters = new URLSearchParams(parameters);
    const { as, c, auth, fetch, tlsOnly, timeout } = int(config);
    const response = await oauth
        .clientCredentialsGrantRequest(as, c, auth, parameters, {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        DPoP: options?.DPoP,
        headers: new Headers(headers),
        signal: signal(timeout),
    })
        .catch(errorHandler);
    const p = oauth.processClientCredentialsResponse(as, c, response);
    let result;
    try {
        result = await p;
    }
    catch (err) {
        if (retryable(err, options)) {
            return clientCredentialsGrant(config, parameters, {
                ...options,
                flag: retry,
            });
        }
        errorHandler(err);
    }
    addHelpers(result);
    return result;
}
export function buildAuthorizationUrl(config, parameters) {
    checkConfig(config);
    const { as, c, tlsOnly, hybrid, jarm, implicit } = int(config);
    const authorizationEndpoint = oauth.resolveEndpoint(as, 'authorization_endpoint', false, tlsOnly);
    parameters = new URLSearchParams(parameters);
    if (!parameters.has('client_id')) {
        parameters.set('client_id', c.client_id);
    }
    if (!parameters.has('request_uri') && !parameters.has('request')) {
        if (!parameters.has('response_type')) {
            parameters.set('response_type', hybrid ? 'code id_token' : implicit ? 'id_token' : 'code');
        }
        if (implicit && !parameters.has('nonce')) {
            throw CodedTypeError('response_type=id_token clients must provide a nonce parameter in their authorization request parameters', ERR_INVALID_ARG_VALUE);
        }
        if (jarm) {
            parameters.set('response_mode', 'jwt');
        }
    }
    for (const [k, v] of parameters.entries()) {
        authorizationEndpoint.searchParams.append(k, v);
    }
    return authorizationEndpoint;
}
export async function buildAuthorizationUrlWithJAR(config, parameters, signingKey, options) {
    checkConfig(config);
    const authorizationEndpoint = buildAuthorizationUrl(config, parameters);
    parameters = authorizationEndpoint.searchParams;
    if (!signingKey) {
        throw CodedTypeError('"signingKey" must be provided', ERR_INVALID_ARG_VALUE);
    }
    const { as, c } = int(config);
    const request = await oauth
        .issueRequestObject(as, c, parameters, signingKey, options)
        .catch(errorHandler);
    return buildAuthorizationUrl(config, { request });
}
export async function buildAuthorizationUrlWithPAR(config, parameters, options) {
    checkConfig(config);
    const authorizationEndpoint = buildAuthorizationUrl(config, parameters);
    const { as, c, auth, fetch, tlsOnly, timeout } = int(config);
    const response = await oauth
        .pushedAuthorizationRequest(as, c, auth, authorizationEndpoint.searchParams, {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        DPoP: options?.DPoP,
        headers: new Headers(headers),
        signal: signal(timeout),
    })
        .catch(errorHandler);
    const p = oauth.processPushedAuthorizationResponse(as, c, response);
    let result;
    try {
        result = await p;
    }
    catch (err) {
        if (retryable(err, options)) {
            return buildAuthorizationUrlWithPAR(config, parameters, {
                ...options,
                flag: retry,
            });
        }
        errorHandler(err);
    }
    return buildAuthorizationUrl(config, { request_uri: result.request_uri });
}
export function buildEndSessionUrl(config, parameters) {
    checkConfig(config);
    const { as, c, tlsOnly } = int(config);
    const endSessionEndpoint = oauth.resolveEndpoint(as, 'end_session_endpoint', false, tlsOnly);
    parameters = new URLSearchParams(parameters);
    if (!parameters.has('client_id')) {
        parameters.set('client_id', c.client_id);
    }
    for (const [k, v] of parameters.entries()) {
        endSessionEndpoint.searchParams.append(k, v);
    }
    return endSessionEndpoint;
}
function checkConfig(input) {
    if (!(input instanceof Configuration)) {
        throw CodedTypeError('"config" must be an instance of Configuration', ERR_INVALID_ARG_TYPE);
    }
    if (Object.getPrototypeOf(input) !== Configuration.prototype) {
        throw CodedTypeError('subclassing Configuration is not allowed', ERR_INVALID_ARG_VALUE);
    }
}
function signal(timeout) {
    return timeout ? AbortSignal.timeout(timeout * 1000) : undefined;
}
export async function fetchUserInfo(config, accessToken, expectedSubject, options) {
    checkConfig(config);
    const { as, c, fetch, tlsOnly, nonRepudiation, timeout, decrypt } = int(config);
    const response = await oauth
        .userInfoRequest(as, c, accessToken, {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        DPoP: options?.DPoP,
        headers: new Headers(headers),
        signal: signal(timeout),
    })
        .catch(errorHandler);
    let exec = oauth.processUserInfoResponse(as, c, expectedSubject, response, {
        [oauth.jweDecrypt]: decrypt,
    });
    let result;
    try {
        result = await exec;
    }
    catch (err) {
        if (retryable(err, options)) {
            return fetchUserInfo(config, accessToken, expectedSubject, {
                ...options,
                flag: retry,
            });
        }
        errorHandler(err);
    }
    oauth.getContentType(response) === 'application/jwt' &&
        (await nonRepudiation?.(response));
    return result;
}
function retryable(err, options) {
    if (options?.DPoP && options.flag !== retry) {
        return oauth.isDPoPNonceError(err);
    }
    return false;
}
export async function tokenIntrospection(config, token, parameters) {
    checkConfig(config);
    const { as, c, auth, fetch, tlsOnly, nonRepudiation, timeout, decrypt } = int(config);
    const response = await oauth
        .introspectionRequest(as, c, auth, token, {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        additionalParameters: new URLSearchParams(parameters),
        headers: new Headers(headers),
        signal: signal(timeout),
    })
        .catch(errorHandler);
    const result = await oauth
        .processIntrospectionResponse(as, c, response, {
        [oauth.jweDecrypt]: decrypt,
    })
        .catch(errorHandler);
    oauth.getContentType(response) === 'application/token-introspection+jwt' &&
        (await nonRepudiation?.(response));
    return result;
}
const retry = Symbol();
export async function genericGrantRequest(config, grantType, parameters, options) {
    checkConfig(config);
    const { as, c, auth, fetch, tlsOnly, timeout, decrypt } = int(config);
    const result = await oauth
        .genericTokenEndpointRequest(as, c, auth, grantType, new URLSearchParams(parameters), {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        DPoP: options?.DPoP,
        headers: new Headers(headers),
        signal: signal(timeout),
    })
        .then((response) => {
        let recognizedTokenTypes;
        if (grantType === 'urn:ietf:params:oauth:grant-type:token-exchange') {
            recognizedTokenTypes = { n_a: () => { } };
        }
        return oauth.processGenericTokenEndpointResponse(as, c, response, {
            [oauth.jweDecrypt]: decrypt,
            recognizedTokenTypes,
        });
    })
        .catch(errorHandler);
    addHelpers(result);
    return result;
}
export async function tokenRevocation(config, token, parameters) {
    checkConfig(config);
    const { as, c, auth, fetch, tlsOnly, timeout } = int(config);
    return oauth
        .revocationRequest(as, c, auth, token, {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        additionalParameters: new URLSearchParams(parameters),
        headers: new Headers(headers),
        signal: signal(timeout),
    })
        .then(oauth.processRevocationResponse)
        .catch(errorHandler);
}
export async function fetchProtectedResource(config, accessToken, url, method, body, headers, options) {
    checkConfig(config);
    headers ||= new Headers();
    if (!headers.has('user-agent')) {
        headers.set('user-agent', USER_AGENT);
    }
    const { fetch, tlsOnly, timeout } = int(config);
    const exec = oauth.protectedResourceRequest(accessToken, method, url, headers, body, {
        [oauth.customFetch]: fetch,
        [oauth.allowInsecureRequests]: !tlsOnly,
        DPoP: options?.DPoP,
        signal: signal(timeout),
    });
    let result;
    try {
        result = await exec;
    }
    catch (err) {
        if (retryable(err, options)) {
            return fetchProtectedResource(config, accessToken, url, method, body, headers, {
                ...options,
                flag: retry,
            });
        }
        errorHandler(err);
    }
    return result;
}
//# sourceMappingURL=index.js.map