"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.JwtVerifier = void 0;
const jose_1 = require("jose");
const lodash_1 = require("lodash");
const index_js_1 = require("../errors/index.js");
/**
 * Verifies JWTs using a JWKS endpoint to resolve signing keys on demand.
 */
class JwtVerifier {
    options;
    // Memoize the remote JWKS loader; without this, we'd recreate it on each verification and jose's own cache would never persist.
    remoteJwkSet;
    constructor(options) {
        this.options = options;
    }
    async verify(token, options) {
        let kid;
        try {
            const header = (0, jose_1.decodeProtectedHeader)(token);
            kid = header.kid;
        }
        catch {
            // Preserve legacy error message relied upon by tests
            throw new Error('Invalid token specified');
        }
        if ((0, lodash_1.isEmpty)(kid)) {
            throw new index_js_1.UnauthorizedError('Invalid JWT as kid header is missing.');
        }
        const jwkSet = await this.resolveJwkSet();
        try {
            const result = await (0, jose_1.jwtVerify)(token, jwkSet, options);
            return result.payload;
        }
        catch (err) {
            throw new index_js_1.UnauthorizedError(`JWT verification failed: ${err.message}`);
        }
    }
    async resolveJwkSet() {
        // Prefer interceptor-provided keys for explicit testability and control
        if (this.options.getKeysInterceptor) {
            const keys = await this.options.getKeysInterceptor();
            if ((0, lodash_1.isEmpty)(keys)) {
                throw new index_js_1.UnauthorizedError('JWT could not be verified as no corresponding public key was found.');
            }
            return (0, jose_1.createLocalJWKSet)({ keys });
        }
        // Allow custom fetcher to provide JWKS out-of-band
        if (this.options.fetcher) {
            const jwks = await this.options.fetcher(this.options.jwksUri);
            const keys = jwks.keys;
            if (!Array.isArray(keys) || (0, lodash_1.isEmpty)(keys)) {
                throw new index_js_1.UnauthorizedError('JWT could not be verified as no corresponding public key was found.');
            }
            return (0, jose_1.createLocalJWKSet)({ keys });
        }
        // Default to remote JWKS. jose v5 supports headers/timeout; pass when provided.
        const url = new URL(this.options.jwksUri);
        const opts = {};
        if (!(0, lodash_1.isEmpty)(this.options.requestHeaders)) {
            opts.headers = this.options.requestHeaders;
        }
        if (!(0, lodash_1.isEmpty)(this.options.timeoutMs)) {
            opts.timeoutDuration = this.options.timeoutMs;
        }
        if (!this.remoteJwkSet) {
            this.remoteJwkSet = (0, jose_1.createRemoteJWKSet)(url, opts);
        }
        return this.remoteJwkSet;
    }
}
exports.JwtVerifier = JwtVerifier;
//# sourceMappingURL=verifier.js.map