/**
 * @fileoverview Implements the OAuth 2.1 authentication strategy.
 * This module provides a concrete implementation of the AuthStrategy for validating
 * JWTs against a remote JSON Web Key Set (JWKS), as is common in OAuth 2.1 flows.
 * @module src/mcp-server/transports/auth/strategies/OauthStrategy
 */
import { createRemoteJWKSet, jwtVerify } from 'jose';
import { buildAuthInfoFromClaims, handleJoseVerifyError, } from '../../../../mcp-server/transports/auth/lib/claimParser.js';
import { configurationError, forbidden, serviceUnavailable } from '../../../../types-global/errors.js';
import { requestContextService } from '../../../../utils/internal/requestContext.js';
export class OauthStrategy {
    config;
    logger;
    jwks;
    issuerUrl;
    audience;
    constructor(config, logger) {
        this.config = config;
        this.logger = logger;
        const context = requestContextService.createRequestContext({
            operation: 'OauthStrategy.constructor',
        });
        this.logger.debug('Initializing OauthStrategy...', context);
        if (this.config.mcpAuthMode !== 'oauth') {
            throw new Error('OauthStrategy instantiated for non-oauth auth mode.');
        }
        if (!this.config.oauthIssuerUrl || !this.config.oauthAudience) {
            this.logger.fatal('CRITICAL: OAUTH_ISSUER_URL and OAUTH_AUDIENCE must be set for OAuth mode.', context);
            throw configurationError('OAUTH_ISSUER_URL and OAUTH_AUDIENCE must be set for OAuth mode.', context);
        }
        // Store validated config — no casts needed after the guard above
        this.issuerUrl = this.config.oauthIssuerUrl;
        this.audience = this.config.oauthAudience;
        try {
            const jwksUrl = new URL(this.config.oauthJwksUri || `${this.issuerUrl.replace(/\/$/, '')}/.well-known/jwks.json`);
            this.jwks = createRemoteJWKSet(jwksUrl, {
                cooldownDuration: this.config.oauthJwksCooldownMs,
                timeoutDuration: this.config.oauthJwksTimeoutMs,
            });
            this.logger.info(`JWKS client initialized for URL: ${jwksUrl.href}`, context);
        }
        catch (error) {
            this.logger.fatal('Failed to initialize JWKS client.', {
                ...context,
                error: error instanceof Error ? error.message : String(error),
            });
            throw serviceUnavailable('Could not initialize JWKS client for OAuth strategy.', {
                ...context,
                originalError: error instanceof Error ? error.message : 'Unknown',
            });
        }
    }
    async verify(token) {
        const context = requestContextService.createRequestContext({
            operation: 'OauthStrategy.verify',
        });
        this.logger.debug('Attempting to verify OAuth token via JWKS.', context);
        try {
            const { payload } = await jwtVerify(token, this.jwks, {
                issuer: this.issuerUrl,
                audience: this.audience,
                algorithms: ['RS256', 'ES256', 'PS256'],
            });
            this.logger.debug('OAuth token signature verified successfully.', {
                ...context,
                claims: {
                    iss: payload.iss,
                    aud: payload.aud,
                    exp: payload.exp,
                    iat: payload.iat,
                    jti: payload.jti,
                },
            });
            // RFC 8707 Resource Indicators validation (MCP 2025-06-18 requirement)
            if (this.config.mcpServerResourceIdentifier) {
                const resourceClaim = payload.resource || payload.aud;
                const expectedResource = this.config.mcpServerResourceIdentifier;
                const isResourceValid = (Array.isArray(resourceClaim) && resourceClaim.includes(expectedResource)) ||
                    resourceClaim === expectedResource;
                if (!isResourceValid) {
                    this.logger.warning('Token resource indicator mismatch. Token was not issued for this MCP server.', {
                        ...context,
                        expected: expectedResource,
                        received: resourceClaim,
                    });
                    throw forbidden('Token was not issued for this MCP server. Resource indicator mismatch.', {
                        expected: expectedResource,
                        received: resourceClaim,
                    });
                }
                this.logger.debug('RFC 8707 resource indicator validated successfully.', {
                    ...context,
                    resource: expectedResource,
                });
            }
            const authInfo = buildAuthInfoFromClaims(token, payload);
            this.logger.info('OAuth token verification successful.', {
                ...context,
                clientId: authInfo.clientId,
                scopes: authInfo.scopes,
                ...(authInfo.tenantId ? { tenantId: authInfo.tenantId } : {}),
            });
            return authInfo;
        }
        catch (error) {
            this.logger.warning('OAuth token verification failed.', {
                ...context,
                errorName: error instanceof Error ? error.name : 'Unknown',
            });
            handleJoseVerifyError(error, 'OAuth token verification failed.');
        }
    }
}
//# sourceMappingURL=oauthStrategy.js.map