tasq/node_modules/fastmcp/dist/chunk-7UDY4VFQ.cjs.map

1 line
128 KiB
Plaintext

{"version":3,"sources":["/home/runner/work/fastmcp/fastmcp/dist/chunk-7UDY4VFQ.cjs","../src/auth/helpers.ts","../src/auth/OAuthProxy.ts","../src/auth/types.ts","../src/auth/utils/claimsExtractor.ts","../src/auth/utils/consent.ts","../src/auth/utils/jwtIssuer.ts","../src/auth/utils/pkce.ts","../src/auth/utils/tokenStore.ts","../src/auth/providers/AuthProvider.ts","../src/auth/providers/AzureProvider.ts","../src/auth/providers/GitHubProvider.ts","../src/auth/providers/GoogleProvider.ts","../src/auth/providers/OAuthProvider.ts","../src/auth/utils/diskStore.ts","../src/auth/utils/jwks.ts"],"names":["randomBytes"],"mappings":"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","file":"/home/runner/work/fastmcp/fastmcp/dist/chunk-7UDY4VFQ.cjs","sourcesContent":[null,"/**\n * Authentication Helper Functions\n * Utility functions for use with canAccess on tools, resources, and prompts\n */\n\nimport type { OAuthSession } from \"./providers/AuthProvider.js\";\n\ntype SessionAuth = Record<string, unknown> | undefined;\n\n/**\n * Extract and type-cast OAuth session from canAccess context.\n * Throws if session is undefined (use with canAccess: requireAuth).\n */\nexport function getAuthSession<T extends OAuthSession = OAuthSession>(\n session: SessionAuth,\n): T {\n if (!session) {\n throw new Error(\"Session is not authenticated\");\n }\n return session as T;\n}\n\n/**\n * Combines multiple canAccess checks with AND logic.\n */\nexport function requireAll<T extends SessionAuth>(\n ...checks: Array<((auth: T) => boolean) | boolean>\n): (auth: T) => boolean {\n return (auth: T): boolean =>\n checks.every((check) =>\n typeof check === \"function\" ? check(auth) : check,\n );\n}\n\n/**\n * Combines multiple canAccess checks with OR logic.\n */\nexport function requireAny<T extends SessionAuth>(\n ...checks: Array<((auth: T) => boolean) | boolean>\n): (auth: T) => boolean {\n return (auth: T): boolean =>\n checks.some((check) => (typeof check === \"function\" ? check(auth) : check));\n}\n\n/**\n * Requires any authenticated session.\n */\nexport function requireAuth<T extends SessionAuth>(auth: T): boolean {\n return auth !== undefined && auth !== null;\n}\n\n/**\n * Requires session to have a specific role (OR logic for multiple roles).\n */\nexport function requireRole<T extends SessionAuth>(\n ...allowedRoles: string[]\n): (auth: T) => boolean {\n return (auth: T): boolean => {\n if (!auth) return false;\n const role = (auth as Record<string, unknown>).role;\n return typeof role === \"string\" && allowedRoles.includes(role);\n };\n}\n\n/**\n * Requires session to have specific scopes.\n */\nexport function requireScopes<T extends SessionAuth>(\n ...requiredScopes: string[]\n): (auth: T) => boolean {\n return (auth: T): boolean => {\n if (!auth) return false;\n const authScopes = (auth as Record<string, unknown>).scopes;\n if (!authScopes) return false;\n\n const scopeSet = Array.isArray(authScopes)\n ? new Set(authScopes)\n : authScopes instanceof Set\n ? authScopes\n : new Set();\n\n return requiredScopes.every((scope) => scopeSet.has(scope));\n };\n}\n","/**\n * OAuth 2.1 Proxy Implementation\n * Provides DCR-compatible interface for non-DCR OAuth providers\n */\n\nimport { randomBytes } from \"crypto\";\nimport { z } from \"zod\";\n\nimport type {\n AuthorizationParams,\n ClientCode,\n DCRRequest,\n DCRResponse,\n OAuthError,\n OAuthProxyConfig,\n OAuthTransaction,\n ProxyDCRClient,\n RefreshRequest,\n TokenRequest,\n TokenResponse,\n TokenStorage,\n UpstreamTokenSet,\n} from \"./types.js\";\n\nimport {\n DEFAULT_ACCESS_TOKEN_TTL,\n DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH,\n DEFAULT_AUTHORIZATION_CODE_TTL,\n DEFAULT_REFRESH_TOKEN_TTL,\n DEFAULT_TRANSACTION_TTL,\n} from \"./types.js\";\nimport { ClaimsExtractor } from \"./utils/claimsExtractor.js\";\nimport { ConsentManager } from \"./utils/consent.js\";\nimport { JWTIssuer } from \"./utils/jwtIssuer.js\";\nimport { PKCEUtils } from \"./utils/pkce.js\";\nimport {\n EncryptedTokenStorage,\n MemoryTokenStorage,\n} from \"./utils/tokenStore.js\";\n\n/**\n * OAuth 2.1 Proxy\n * Acts as transparent intermediary between MCP clients and upstream OAuth providers\n */\nexport class OAuthProxy {\n private claimsExtractor: ClaimsExtractor | null = null;\n private cleanupInterval: NodeJS.Timeout | null = null;\n private clientCodes: Map<string, ClientCode> = new Map();\n private config: OAuthProxyConfig;\n private consentManager: ConsentManager;\n private jwtIssuer?: JWTIssuer;\n private registeredClients: Map<string, ProxyDCRClient> = new Map();\n private tokenStorage: TokenStorage;\n private transactions: Map<string, OAuthTransaction> = new Map();\n\n constructor(config: OAuthProxyConfig) {\n this.config = {\n allowedRedirectUriPatterns: [\"https://*\", \"http://localhost:*\"],\n authorizationCodeTtl: DEFAULT_AUTHORIZATION_CODE_TTL,\n consentRequired: true,\n enableTokenSwap: true, // Enabled by default for security\n redirectPath: \"/oauth/callback\",\n transactionTtl: DEFAULT_TRANSACTION_TTL,\n upstreamTokenEndpointAuthMethod: \"client_secret_basic\",\n ...config,\n };\n\n // Set up token storage with encryption by default (matches Python's secure defaults)\n let storage = config.tokenStorage || new MemoryTokenStorage();\n\n // Wrap storage with encryption if not already encrypted\n // Check if it's already an EncryptedTokenStorage instance\n const isAlreadyEncrypted =\n storage.constructor.name === \"EncryptedTokenStorage\";\n\n if (!isAlreadyEncrypted && config.encryptionKey !== false) {\n // Auto-generate encryption key if not provided\n const encryptionKey =\n typeof config.encryptionKey === \"string\"\n ? config.encryptionKey\n : this.generateSigningKey();\n\n storage = new EncryptedTokenStorage(storage, encryptionKey);\n }\n\n this.tokenStorage = storage;\n this.consentManager = new ConsentManager(\n config.consentSigningKey || this.generateSigningKey(),\n );\n\n // Initialize JWT issuer if token swap is enabled\n if (this.config.enableTokenSwap) {\n // Auto-generate signing key if not provided\n const signingKey = this.config.jwtSigningKey || this.generateSigningKey();\n\n this.jwtIssuer = new JWTIssuer({\n audience: this.config.baseUrl,\n issuer: this.config.baseUrl,\n signingKey: signingKey,\n });\n }\n\n // Initialize claims extractor (enabled by default)\n const claimsConfig =\n config.customClaimsPassthrough !== undefined\n ? config.customClaimsPassthrough\n : true; // Default: enabled\n\n if (claimsConfig !== false) {\n this.claimsExtractor = new ClaimsExtractor(claimsConfig);\n }\n\n // Start periodic cleanup\n this.startCleanup();\n }\n\n /**\n * OAuth authorization endpoint\n */\n async authorize(params: AuthorizationParams): Promise<Response> {\n // Validate parameters\n if (!params.client_id || !params.redirect_uri || !params.response_type) {\n throw new OAuthProxyError(\n \"invalid_request\",\n \"Missing required parameters\",\n );\n }\n\n if (params.response_type !== \"code\") {\n throw new OAuthProxyError(\n \"unsupported_response_type\",\n \"Only 'code' response type is supported\",\n );\n }\n\n // Validate PKCE if provided\n if (params.code_challenge && !params.code_challenge_method) {\n throw new OAuthProxyError(\n \"invalid_request\",\n \"code_challenge_method required when code_challenge is present\",\n );\n }\n\n // Create transaction\n const transaction = await this.createTransaction(params);\n\n // If consent required, show consent screen\n if (this.config.consentRequired && !transaction.consentGiven) {\n return this.consentManager.createConsentResponse(\n transaction,\n this.getProviderName(),\n );\n }\n\n // Redirect to upstream provider\n return this.redirectToUpstream(transaction);\n }\n\n /**\n * Stop cleanup interval and destroy resources\n */\n destroy(): void {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = null;\n }\n\n this.transactions.clear();\n this.clientCodes.clear();\n this.registeredClients.clear();\n }\n\n /**\n * Token endpoint - exchange authorization code for tokens\n */\n async exchangeAuthorizationCode(\n request: TokenRequest,\n ): Promise<TokenResponse> {\n if (request.grant_type !== \"authorization_code\") {\n throw new OAuthProxyError(\n \"unsupported_grant_type\",\n \"Only authorization_code grant type is supported\",\n );\n }\n\n const clientCode = this.clientCodes.get(request.code);\n if (!clientCode) {\n throw new OAuthProxyError(\n \"invalid_grant\",\n \"Invalid or expired authorization code\",\n );\n }\n\n // Validate client\n if (clientCode.clientId !== request.client_id) {\n throw new OAuthProxyError(\"invalid_client\", \"Client ID mismatch\");\n }\n\n // Validate PKCE if used\n if (clientCode.codeChallenge) {\n if (!request.code_verifier) {\n throw new OAuthProxyError(\n \"invalid_request\",\n \"code_verifier required for PKCE\",\n );\n }\n\n const valid = PKCEUtils.validateChallenge(\n request.code_verifier,\n clientCode.codeChallenge,\n clientCode.codeChallengeMethod,\n );\n\n if (!valid) {\n throw new OAuthProxyError(\"invalid_grant\", \"Invalid PKCE verifier\");\n }\n }\n\n // Check if code was already used\n if (clientCode.used) {\n throw new OAuthProxyError(\n \"invalid_grant\",\n \"Authorization code already used\",\n );\n }\n\n // Mark code as used\n clientCode.used = true;\n this.clientCodes.set(request.code, clientCode);\n\n // Return tokens based on token swap setting\n if (this.config.enableTokenSwap && this.jwtIssuer) {\n // Token swap pattern: issue short-lived JWTs and store upstream tokens\n return await this.issueSwappedTokens(\n clientCode.clientId,\n clientCode.upstreamTokens,\n );\n } else {\n // Pass-through pattern: return upstream tokens directly\n const response: TokenResponse = {\n access_token: clientCode.upstreamTokens.accessToken,\n expires_in: clientCode.upstreamTokens.expiresIn,\n token_type: clientCode.upstreamTokens.tokenType,\n };\n\n if (clientCode.upstreamTokens.refreshToken) {\n response.refresh_token = clientCode.upstreamTokens.refreshToken;\n }\n\n if (clientCode.upstreamTokens.idToken) {\n response.id_token = clientCode.upstreamTokens.idToken;\n }\n\n if (clientCode.upstreamTokens.scope.length > 0) {\n response.scope = clientCode.upstreamTokens.scope.join(\" \");\n }\n\n return response;\n }\n }\n\n /**\n * Token endpoint - refresh access token\n */\n async exchangeRefreshToken(request: RefreshRequest): Promise<TokenResponse> {\n if (request.grant_type !== \"refresh_token\") {\n throw new OAuthProxyError(\n \"unsupported_grant_type\",\n \"Only refresh_token grant type is supported\",\n );\n }\n\n // Check for swap mode\n if (this.config.enableTokenSwap && this.jwtIssuer) {\n return await this.handleSwapModeRefresh(request);\n }\n\n // Passthrough mode: forward refresh token directly to upstream\n return await this.handlePassthroughRefresh(request);\n }\n\n /**\n * Get OAuth discovery metadata\n */\n getAuthorizationServerMetadata(): {\n authorizationEndpoint: string;\n codeChallengeMethodsSupported?: string[];\n dpopSigningAlgValuesSupported?: string[];\n grantTypesSupported?: string[];\n introspectionEndpoint?: string;\n issuer: string;\n jwksUri?: string;\n opPolicyUri?: string;\n opTosUri?: string;\n registrationEndpoint?: string;\n responseModesSupported?: string[];\n responseTypesSupported: string[];\n revocationEndpoint?: string;\n scopesSupported?: string[];\n serviceDocumentation?: string;\n tokenEndpoint: string;\n tokenEndpointAuthMethodsSupported?: string[];\n tokenEndpointAuthSigningAlgValuesSupported?: string[];\n uiLocalesSupported?: string[];\n } {\n return {\n authorizationEndpoint: `${this.config.baseUrl}/oauth/authorize`,\n codeChallengeMethodsSupported: [\"S256\", \"plain\"],\n grantTypesSupported: [\"authorization_code\", \"refresh_token\"],\n issuer: this.config.baseUrl,\n registrationEndpoint: `${this.config.baseUrl}/oauth/register`,\n responseTypesSupported: [\"code\"],\n scopesSupported: this.config.scopes || [],\n tokenEndpoint: `${this.config.baseUrl}/oauth/token`,\n tokenEndpointAuthMethodsSupported: [\n \"client_secret_basic\",\n \"client_secret_post\",\n ],\n };\n }\n\n /**\n * Handle OAuth callback from upstream provider\n */\n async handleCallback(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const code = url.searchParams.get(\"code\");\n const state = url.searchParams.get(\"state\");\n const error = url.searchParams.get(\"error\");\n\n // Check for errors from upstream\n if (error) {\n const errorDescription = url.searchParams.get(\"error_description\");\n throw new OAuthProxyError(error, errorDescription || undefined);\n }\n\n if (!code || !state) {\n throw new OAuthProxyError(\n \"invalid_request\",\n \"Missing code or state parameter\",\n );\n }\n\n // Retrieve transaction\n const transaction = this.transactions.get(state);\n if (!transaction) {\n throw new OAuthProxyError(\"invalid_request\", \"Invalid or expired state\");\n }\n\n // Exchange code with upstream provider\n const upstreamTokens = await this.exchangeUpstreamCode(code, transaction);\n\n // Generate authorization code for client\n const clientCode = this.generateAuthorizationCode(\n transaction,\n upstreamTokens,\n );\n\n // Clean up transaction\n this.transactions.delete(state);\n\n // Redirect to client callback with code\n const redirectUrl = new URL(transaction.clientCallbackUrl);\n redirectUrl.searchParams.set(\"code\", clientCode);\n redirectUrl.searchParams.set(\"state\", transaction.state);\n\n return new Response(null, {\n headers: {\n Location: redirectUrl.toString(),\n },\n status: 302,\n });\n }\n\n /**\n * Handle consent form submission\n */\n async handleConsent(request: Request): Promise<Response> {\n const formData = await request.formData();\n const transactionId = formData.get(\"transaction_id\") as string;\n const action = formData.get(\"action\") as string;\n\n if (!transactionId) {\n throw new OAuthProxyError(\"invalid_request\", \"Missing transaction_id\");\n }\n\n const transaction = this.transactions.get(transactionId);\n if (!transaction) {\n throw new OAuthProxyError(\n \"invalid_request\",\n \"Invalid or expired transaction\",\n );\n }\n\n if (action === \"deny\") {\n // User denied consent\n this.transactions.delete(transactionId);\n const redirectUrl = new URL(transaction.clientCallbackUrl);\n redirectUrl.searchParams.set(\"error\", \"access_denied\");\n redirectUrl.searchParams.set(\n \"error_description\",\n \"User denied authorization\",\n );\n redirectUrl.searchParams.set(\"state\", transaction.state);\n\n return new Response(null, {\n headers: {\n Location: redirectUrl.toString(),\n },\n status: 302,\n });\n }\n\n // User approved, mark consent and redirect to upstream\n transaction.consentGiven = true;\n this.transactions.set(transactionId, transaction);\n\n return this.redirectToUpstream(transaction);\n }\n\n /**\n * Load upstream tokens from a FastMCP JWT\n */\n async loadUpstreamTokens(\n fastmcpToken: string,\n ): Promise<null | UpstreamTokenSet> {\n if (!this.jwtIssuer) {\n return null;\n }\n\n // Verify FastMCP JWT\n const result = await this.jwtIssuer.verify(fastmcpToken);\n if (!result.valid || !result.claims?.jti) {\n return null;\n }\n\n // Look up token mapping\n const mapping = (await this.tokenStorage.get(\n `mapping:${result.claims.jti}`,\n )) as {\n upstreamTokenKey: string;\n } | null;\n\n if (!mapping) {\n return null;\n }\n\n // Retrieve upstream tokens\n const upstreamTokens = (await this.tokenStorage.get(\n `upstream:${mapping.upstreamTokenKey}`,\n )) as null | UpstreamTokenSet;\n\n return upstreamTokens;\n }\n\n /**\n * RFC 7591 Dynamic Client Registration\n */\n async registerClient(request: DCRRequest): Promise<DCRResponse> {\n // Validate required fields\n if (!request.redirect_uris || request.redirect_uris.length === 0) {\n throw new OAuthProxyError(\n \"invalid_client_metadata\",\n \"redirect_uris is required\",\n );\n }\n\n // Validate redirect URIs\n for (const uri of request.redirect_uris) {\n if (!this.validateRedirectUri(uri)) {\n throw new OAuthProxyError(\n \"invalid_redirect_uri\",\n `Invalid redirect URI: ${uri}`,\n );\n }\n }\n\n // Store client registration (indexed by primary redirect URI)\n const clientId = this.config.upstreamClientId;\n const client: ProxyDCRClient = {\n callbackUrl: request.redirect_uris[0],\n clientId,\n clientSecret: this.config.upstreamClientSecret,\n metadata: {\n client_name: request.client_name,\n client_uri: request.client_uri,\n contacts: request.contacts,\n jwks: request.jwks,\n jwks_uri: request.jwks_uri,\n logo_uri: request.logo_uri,\n policy_uri: request.policy_uri,\n scope: request.scope,\n software_id: request.software_id,\n software_version: request.software_version,\n tos_uri: request.tos_uri,\n },\n registeredAt: new Date(),\n };\n\n this.registeredClients.set(request.redirect_uris[0], client);\n\n // Return RFC 7591 compliant response\n const response: DCRResponse = {\n client_id: clientId,\n client_id_issued_at: Math.floor(Date.now() / 1000),\n // Echo back optional metadata\n client_name: request.client_name,\n client_secret: this.config.upstreamClientSecret,\n client_secret_expires_at: 0, // Never expires\n client_uri: request.client_uri,\n contacts: request.contacts,\n grant_types: request.grant_types || [\n \"authorization_code\",\n \"refresh_token\",\n ],\n jwks: request.jwks,\n jwks_uri: request.jwks_uri,\n logo_uri: request.logo_uri,\n policy_uri: request.policy_uri,\n redirect_uris: request.redirect_uris,\n response_types: request.response_types || [\"code\"],\n scope: request.scope,\n software_id: request.software_id,\n software_version: request.software_version,\n token_endpoint_auth_method:\n request.token_endpoint_auth_method || \"client_secret_basic\",\n tos_uri: request.tos_uri,\n };\n\n return response;\n }\n\n /**\n * Calculate access token TTL from upstream tokens\n */\n private calculateAccessTokenTtl(upstreamTokens: UpstreamTokenSet): number {\n if (upstreamTokens.expiresIn > 0) {\n return upstreamTokens.expiresIn;\n } else if (this.config.accessTokenTtl) {\n return this.config.accessTokenTtl;\n } else if (upstreamTokens.refreshToken) {\n return DEFAULT_ACCESS_TOKEN_TTL;\n } else {\n return DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH;\n }\n }\n\n /**\n * Clean up expired transactions and codes\n */\n private cleanup(): void {\n const now = Date.now();\n\n // Clean up expired transactions\n for (const [id, transaction] of this.transactions.entries()) {\n if (transaction.expiresAt.getTime() < now) {\n this.transactions.delete(id);\n }\n }\n\n // Clean up expired codes\n for (const [code, clientCode] of this.clientCodes.entries()) {\n if (clientCode.expiresAt.getTime() < now) {\n this.clientCodes.delete(code);\n }\n }\n\n // Clean up token storage\n void this.tokenStorage.cleanup();\n }\n\n /**\n * Create a new OAuth transaction\n */\n private async createTransaction(\n params: AuthorizationParams,\n ): Promise<OAuthTransaction> {\n const transactionId = this.generateId();\n const proxyPkce = PKCEUtils.generatePair(\"S256\");\n\n const transaction: OAuthTransaction = {\n clientCallbackUrl: params.redirect_uri,\n clientCodeChallenge: params.code_challenge || \"\",\n clientCodeChallengeMethod: params.code_challenge_method || \"plain\",\n clientId: params.client_id,\n createdAt: new Date(),\n expiresAt: new Date(\n Date.now() + (this.config.transactionTtl || 600) * 1000,\n ),\n id: transactionId,\n proxyCodeChallenge: proxyPkce.challenge,\n proxyCodeVerifier: proxyPkce.verifier,\n scope: params.scope ? params.scope.split(\" \") : this.config.scopes || [],\n state: params.state || this.generateId(),\n };\n\n this.transactions.set(transactionId, transaction);\n\n return transaction;\n }\n\n /**\n * Exchange authorization code with upstream provider\n */\n private async exchangeUpstreamCode(\n code: string,\n transaction: OAuthTransaction,\n ): Promise<UpstreamTokenSet> {\n const useBasicAuth =\n this.config.upstreamTokenEndpointAuthMethod === \"client_secret_basic\";\n\n const bodyParams: Record<string, string> = {\n code,\n code_verifier: transaction.proxyCodeVerifier,\n grant_type: \"authorization_code\",\n redirect_uri: `${this.config.baseUrl}${this.config.redirectPath}`,\n };\n\n // Include client credentials in body only for client_secret_post\n if (!useBasicAuth) {\n bodyParams.client_id = this.config.upstreamClientId;\n bodyParams.client_secret = this.config.upstreamClientSecret;\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n };\n\n // Add Basic Auth header for client_secret_basic\n if (useBasicAuth) {\n headers[\"Authorization\"] = this.getBasicAuthHeader();\n }\n\n const tokenResponse = await fetch(this.config.upstreamTokenEndpoint, {\n body: new URLSearchParams(bodyParams),\n headers,\n method: \"POST\",\n });\n\n if (!tokenResponse.ok) {\n const error = (await tokenResponse.json()) as {\n error?: string;\n error_description?: string;\n };\n throw new OAuthProxyError(\n error.error || \"server_error\",\n error.error_description,\n );\n }\n\n const tokens = await this.parseTokenResponse(tokenResponse);\n\n return {\n accessToken: tokens.access_token,\n expiresIn: tokens.expires_in || 3600,\n idToken: tokens.id_token,\n issuedAt: new Date(),\n refreshExpiresIn: tokens.refresh_expires_in,\n refreshToken: tokens.refresh_token,\n scope: tokens.scope ? tokens.scope.split(\" \") : transaction.scope,\n tokenType: tokens.token_type || \"Bearer\",\n };\n }\n\n /**\n * Extract JTI from a JWT token\n */\n private async extractJti(token: string): Promise<string> {\n if (!this.jwtIssuer) {\n throw new Error(\"JWT issuer not initialized\");\n }\n\n const result = await this.jwtIssuer.verify(token);\n if (!result.valid || !result.claims?.jti) {\n throw new Error(\"Failed to extract JTI from token\");\n }\n\n return result.claims.jti;\n }\n\n /**\n * Extract custom claims from upstream tokens\n * Combines claims from access token and ID token (if present)\n */\n private async extractUpstreamClaims(\n upstreamTokens: UpstreamTokenSet,\n ): Promise<null | Record<string, unknown>> {\n if (!this.claimsExtractor) {\n return null;\n }\n\n const allClaims: Record<string, unknown> = {};\n\n // Extract from access token (if JWT format)\n const accessClaims = await this.claimsExtractor.extract(\n upstreamTokens.accessToken,\n \"access\",\n );\n if (accessClaims) {\n Object.assign(allClaims, accessClaims);\n }\n\n // Extract from ID token (if present and JWT format)\n if (upstreamTokens.idToken) {\n const idClaims = await this.claimsExtractor.extract(\n upstreamTokens.idToken,\n \"id\",\n );\n if (idClaims) {\n // Access token claims take precedence over ID token claims\n for (const [key, value] of Object.entries(idClaims)) {\n if (!(key in allClaims)) {\n allClaims[key] = value;\n }\n }\n }\n }\n\n return Object.keys(allClaims).length > 0 ? allClaims : null;\n }\n\n /**\n * Generate authorization code for client\n */\n private generateAuthorizationCode(\n transaction: OAuthTransaction,\n upstreamTokens: UpstreamTokenSet,\n ): string {\n const code = this.generateId();\n\n const clientCode: ClientCode = {\n clientId: transaction.clientId,\n code,\n codeChallenge: transaction.clientCodeChallenge,\n codeChallengeMethod: transaction.clientCodeChallengeMethod,\n createdAt: new Date(),\n expiresAt: new Date(\n Date.now() + (this.config.authorizationCodeTtl || 300) * 1000,\n ),\n transactionId: transaction.id,\n upstreamTokens,\n };\n\n this.clientCodes.set(code, clientCode);\n\n return code;\n }\n\n /**\n * Generate secure random ID\n */\n private generateId(): string {\n return randomBytes(32).toString(\"base64url\");\n }\n\n /**\n * Generate signing key for consent cookies\n */\n private generateSigningKey(): string {\n return randomBytes(32).toString(\"hex\");\n }\n\n /**\n * Generate Basic auth header value for upstream token endpoint\n * Per RFC 6749 Section 2.3.1, credentials must be URL-encoded before base64 encoding\n */\n private getBasicAuthHeader(): string {\n const encodedClientId = encodeURIComponent(this.config.upstreamClientId);\n const encodedClientSecret = encodeURIComponent(\n this.config.upstreamClientSecret,\n );\n return `Basic ${Buffer.from(`${encodedClientId}:${encodedClientSecret}`).toString(\"base64\")}`;\n }\n\n /**\n * Get provider name for display\n */\n private getProviderName(): string {\n const url = new URL(this.config.upstreamAuthorizationEndpoint);\n return url.hostname;\n }\n\n /**\n * Handle passthrough mode refresh - forward refresh token directly to upstream\n */\n private async handlePassthroughRefresh(\n request: RefreshRequest,\n ): Promise<TokenResponse> {\n const useBasicAuth =\n this.config.upstreamTokenEndpointAuthMethod === \"client_secret_basic\";\n\n const bodyParams: Record<string, string> = {\n grant_type: \"refresh_token\",\n refresh_token: request.refresh_token,\n ...(request.scope && { scope: request.scope }),\n };\n\n // Include client credentials in body only for client_secret_post\n if (!useBasicAuth) {\n bodyParams.client_id = this.config.upstreamClientId;\n bodyParams.client_secret = this.config.upstreamClientSecret;\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n };\n\n // Add Basic Auth header for client_secret_basic\n if (useBasicAuth) {\n headers[\"Authorization\"] = this.getBasicAuthHeader();\n }\n\n // Exchange refresh token with upstream provider\n const tokenResponse = await fetch(this.config.upstreamTokenEndpoint, {\n body: new URLSearchParams(bodyParams),\n headers,\n method: \"POST\",\n });\n\n if (!tokenResponse.ok) {\n const error = (await tokenResponse.json()) as {\n error?: string;\n error_description?: string;\n };\n throw new OAuthProxyError(\n error.error || \"invalid_grant\",\n error.error_description,\n );\n }\n\n const tokens = await this.parseTokenResponse(tokenResponse);\n\n return {\n access_token: tokens.access_token,\n expires_in: tokens.expires_in || 3600,\n id_token: tokens.id_token,\n refresh_token: tokens.refresh_token,\n scope: tokens.scope,\n token_type: tokens.token_type || \"Bearer\",\n };\n }\n\n /**\n * Handle swap mode refresh - verify FastMCP JWT and issue new tokens\n */\n private async handleSwapModeRefresh(\n request: RefreshRequest,\n ): Promise<TokenResponse> {\n if (!this.jwtIssuer) {\n throw new Error(\"JWT issuer not initialized\");\n }\n\n const verifyResult = await this.jwtIssuer.verify(request.refresh_token);\n if (!verifyResult.valid) {\n throw new OAuthProxyError(\n \"invalid_grant\",\n \"Invalid or expired refresh token\",\n );\n }\n\n const jti = verifyResult.claims?.jti;\n if (!jti) {\n throw new OAuthProxyError(\"invalid_grant\", \"Refresh token missing JTI\");\n }\n\n const mapping = (await this.tokenStorage.get(`mapping:${jti}`)) as {\n clientId: string;\n scope: string[];\n upstreamTokenKey: string;\n } | null;\n\n if (!mapping) {\n throw new OAuthProxyError(\n \"invalid_grant\",\n \"Refresh token already used or expired\",\n );\n }\n\n const upstreamTokens = (await this.tokenStorage.get(\n `upstream:${mapping.upstreamTokenKey}`,\n )) as null | UpstreamTokenSet;\n\n if (!upstreamTokens) {\n throw new OAuthProxyError(\n \"invalid_grant\",\n \"Upstream tokens not found or expired\",\n );\n }\n\n if (!upstreamTokens.refreshToken) {\n throw new OAuthProxyError(\n \"invalid_grant\",\n \"No upstream refresh token available\",\n );\n }\n\n const refreshedUpstreamTokens = await this.refreshUpstreamTokens(\n upstreamTokens.refreshToken,\n request.scope,\n );\n\n if (refreshedUpstreamTokens.scope.length === 0) {\n refreshedUpstreamTokens.scope = upstreamTokens.scope;\n }\n\n const refreshTokenTtl =\n refreshedUpstreamTokens.refreshExpiresIn ??\n this.config.refreshTokenTtl ??\n DEFAULT_REFRESH_TOKEN_TTL;\n const accessTokenTtl = this.calculateAccessTokenTtl(\n refreshedUpstreamTokens,\n );\n const upstreamStorageTtl = Math.max(accessTokenTtl, refreshTokenTtl, 1);\n\n await this.tokenStorage.save(\n `upstream:${mapping.upstreamTokenKey}`,\n refreshedUpstreamTokens,\n upstreamStorageTtl,\n );\n\n return await this.issueSwappedTokensForRefresh(\n mapping.clientId,\n refreshedUpstreamTokens,\n mapping.upstreamTokenKey,\n jti,\n );\n }\n\n /**\n * Issue swapped tokens (JWT pattern)\n * Issues short-lived FastMCP JWTs and stores upstream tokens securely\n */\n private async issueSwappedTokens(\n clientId: string,\n upstreamTokens: UpstreamTokenSet,\n ): Promise<TokenResponse> {\n if (!this.jwtIssuer) {\n throw new Error(\"JWT issuer not initialized\");\n }\n\n // Extract custom claims from upstream tokens\n const customClaims = await this.extractUpstreamClaims(upstreamTokens);\n\n // Determine access token TTL (hierarchical: upstream → config → default)\n let accessTokenTtl: number;\n if (upstreamTokens.expiresIn > 0) {\n accessTokenTtl = upstreamTokens.expiresIn;\n } else if (this.config.accessTokenTtl) {\n accessTokenTtl = this.config.accessTokenTtl;\n } else if (upstreamTokens.refreshToken) {\n accessTokenTtl = DEFAULT_ACCESS_TOKEN_TTL;\n } else {\n accessTokenTtl = DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH;\n }\n\n // Determine refresh token TTL early (needed for upstream storage TTL)\n // Use upstream's refresh_expires_in if provided, otherwise fall back to config/default\n const refreshTokenTtl = upstreamTokens.refreshToken\n ? (upstreamTokens.refreshExpiresIn ??\n this.config.refreshTokenTtl ??\n DEFAULT_REFRESH_TOKEN_TTL)\n : 0;\n\n // Store upstream tokens with longest-lived token TTL (min 1s for safety)\n const upstreamStorageTtl = Math.max(accessTokenTtl, refreshTokenTtl, 1);\n const upstreamTokenKey = this.generateId();\n await this.tokenStorage.save(\n `upstream:${upstreamTokenKey}`,\n upstreamTokens,\n upstreamStorageTtl,\n );\n\n // Issue FastMCP access token with custom claims\n const accessToken = this.jwtIssuer.issueAccessToken(\n clientId,\n upstreamTokens.scope,\n customClaims || undefined,\n accessTokenTtl,\n );\n\n // Decode JWT to get JTI\n const accessJti = await this.extractJti(accessToken);\n\n // Store token mapping\n await this.tokenStorage.save(\n `mapping:${accessJti}`,\n {\n clientId,\n createdAt: new Date(),\n expiresAt: new Date(Date.now() + accessTokenTtl * 1000),\n jti: accessJti,\n scope: upstreamTokens.scope,\n upstreamTokenKey,\n },\n accessTokenTtl,\n );\n\n const response: TokenResponse = {\n access_token: accessToken,\n expires_in: accessTokenTtl,\n scope: upstreamTokens.scope.join(\" \"),\n token_type: \"Bearer\",\n };\n\n // Issue refresh token if upstream provided one\n if (upstreamTokens.refreshToken) {\n const refreshToken = this.jwtIssuer.issueRefreshToken(\n clientId,\n upstreamTokens.scope,\n customClaims || undefined,\n refreshTokenTtl,\n );\n const refreshJti = await this.extractJti(refreshToken);\n\n // Store refresh token mapping\n await this.tokenStorage.save(\n `mapping:${refreshJti}`,\n {\n clientId,\n createdAt: new Date(),\n expiresAt: new Date(Date.now() + refreshTokenTtl * 1000),\n jti: refreshJti,\n scope: upstreamTokens.scope,\n upstreamTokenKey,\n },\n refreshTokenTtl,\n );\n\n response.refresh_token = refreshToken;\n }\n\n return response;\n }\n\n /**\n * Issue swapped tokens for refresh flow\n */\n private async issueSwappedTokensForRefresh(\n clientId: string,\n upstreamTokens: UpstreamTokenSet,\n upstreamTokenKey: string,\n oldJti: string,\n ): Promise<TokenResponse> {\n if (!this.jwtIssuer) {\n throw new Error(\"JWT issuer not initialized\");\n }\n\n await this.tokenStorage.delete(`mapping:${oldJti}`);\n\n const customClaims = await this.extractUpstreamClaims(upstreamTokens);\n\n const accessTokenTtl = this.calculateAccessTokenTtl(upstreamTokens);\n const refreshTokenTtl = upstreamTokens.refreshToken\n ? (upstreamTokens.refreshExpiresIn ??\n this.config.refreshTokenTtl ??\n DEFAULT_REFRESH_TOKEN_TTL)\n : 0;\n\n const accessToken = this.jwtIssuer.issueAccessToken(\n clientId,\n upstreamTokens.scope,\n customClaims || undefined,\n accessTokenTtl,\n );\n\n const accessJti = await this.extractJti(accessToken);\n await this.tokenStorage.save(\n `mapping:${accessJti}`,\n {\n clientId,\n createdAt: new Date(),\n expiresAt: new Date(Date.now() + accessTokenTtl * 1000),\n jti: accessJti,\n scope: upstreamTokens.scope,\n upstreamTokenKey,\n },\n accessTokenTtl,\n );\n\n const response: TokenResponse = {\n access_token: accessToken,\n expires_in: accessTokenTtl,\n scope: upstreamTokens.scope.join(\" \"),\n token_type: \"Bearer\",\n };\n\n if (upstreamTokens.refreshToken) {\n const refreshToken = this.jwtIssuer.issueRefreshToken(\n clientId,\n upstreamTokens.scope,\n customClaims || undefined,\n refreshTokenTtl,\n );\n const refreshJti = await this.extractJti(refreshToken);\n\n await this.tokenStorage.save(\n `mapping:${refreshJti}`,\n {\n clientId,\n createdAt: new Date(),\n expiresAt: new Date(Date.now() + refreshTokenTtl * 1000),\n jti: refreshJti,\n scope: upstreamTokens.scope,\n upstreamTokenKey,\n },\n refreshTokenTtl,\n );\n\n response.refresh_token = refreshToken;\n }\n\n return response;\n }\n\n /**\n * Match URI against pattern (supports wildcards)\n */\n private matchesPattern(uri: string, pattern: string): boolean {\n const regex = new RegExp(\n \"^\" + pattern.replace(/\\*/g, \".*\").replace(/\\?/g, \".\") + \"$\",\n );\n return regex.test(uri);\n }\n\n /**\n * Parse token response that can be either JSON or URL-encoded\n * GitHub Apps return URL-encoded format, most providers return JSON\n */\n private async parseTokenResponse(response: Response): Promise<{\n access_token: string;\n expires_in?: number;\n id_token?: string;\n refresh_expires_in?: number;\n refresh_token?: string;\n scope?: string;\n token_type?: string;\n }> {\n const contentType = (\n response.headers.get(\"content-type\") || \"\"\n ).toLowerCase();\n\n // Define Zod schema for token response validation\n const tokenResponseSchema = z.object({\n access_token: z.string().min(1, \"access_token cannot be empty\"),\n expires_in: z.coerce.number().int().positive().optional(),\n id_token: z.string().optional(),\n refresh_expires_in: z.coerce.number().int().positive().optional(),\n refresh_token: z.string().optional(),\n scope: z.string().optional(),\n token_type: z.string().optional(),\n });\n\n // Check if response is URL-encoded (e.g., GitHub Apps)\n if (contentType.includes(\"application/x-www-form-urlencoded\")) {\n const text = await response.text();\n const params = new URLSearchParams(text);\n\n const rawData = {\n access_token: params.get(\"access_token\") || \"\",\n expires_in: params.get(\"expires_in\")\n ? parseInt(params.get(\"expires_in\")!)\n : undefined,\n id_token: params.get(\"id_token\") || undefined,\n refresh_expires_in: params.get(\"refresh_expires_in\")\n ? parseInt(params.get(\"refresh_expires_in\")!)\n : undefined,\n refresh_token: params.get(\"refresh_token\") || undefined,\n scope: params.get(\"scope\") || undefined,\n token_type: params.get(\"token_type\") || undefined,\n };\n\n return tokenResponseSchema.parse(rawData);\n }\n\n // Default to JSON parsing\n const rawJson = await response.json();\n return tokenResponseSchema.parse(rawJson);\n }\n\n /**\n * Redirect to upstream OAuth provider\n */\n private redirectToUpstream(transaction: OAuthTransaction): Response {\n const authUrl = new URL(this.config.upstreamAuthorizationEndpoint);\n\n authUrl.searchParams.set(\"client_id\", this.config.upstreamClientId);\n authUrl.searchParams.set(\n \"redirect_uri\",\n `${this.config.baseUrl}${this.config.redirectPath}`,\n );\n authUrl.searchParams.set(\"response_type\", \"code\");\n authUrl.searchParams.set(\"state\", transaction.id);\n\n if (transaction.scope.length > 0) {\n authUrl.searchParams.set(\"scope\", transaction.scope.join(\" \"));\n }\n\n // Add PKCE if not forwarding client PKCE\n if (!this.config.forwardPkce) {\n authUrl.searchParams.set(\n \"code_challenge\",\n transaction.proxyCodeChallenge,\n );\n authUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n }\n\n return new Response(null, {\n headers: {\n Location: authUrl.toString(),\n },\n status: 302,\n });\n }\n\n /**\n * Refresh upstream tokens with provider\n */\n private async refreshUpstreamTokens(\n upstreamRefreshToken: string,\n requestedScope?: string,\n ): Promise<UpstreamTokenSet> {\n const useBasicAuth =\n this.config.upstreamTokenEndpointAuthMethod === \"client_secret_basic\";\n\n const bodyParams: Record<string, string> = {\n grant_type: \"refresh_token\",\n refresh_token: upstreamRefreshToken,\n ...(requestedScope && { scope: requestedScope }),\n };\n\n // Include client credentials in body only for client_secret_post\n if (!useBasicAuth) {\n bodyParams.client_id = this.config.upstreamClientId;\n bodyParams.client_secret = this.config.upstreamClientSecret;\n }\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n };\n\n // Add Basic Auth header for client_secret_basic\n if (useBasicAuth) {\n headers[\"Authorization\"] = this.getBasicAuthHeader();\n }\n\n // Exchange refresh token with upstream provider\n const tokenResponse = await fetch(this.config.upstreamTokenEndpoint, {\n body: new URLSearchParams(bodyParams),\n headers,\n method: \"POST\",\n });\n\n if (!tokenResponse.ok) {\n const error = (await tokenResponse.json()) as {\n error?: string;\n error_description?: string;\n };\n throw new OAuthProxyError(\n error.error || \"invalid_grant\",\n error.error_description || \"Upstream refresh failed\",\n );\n }\n\n const tokens = await this.parseTokenResponse(tokenResponse);\n\n // Handle token rotation: if upstream doesn't return new refresh token,\n // preserve the original one\n return {\n accessToken: tokens.access_token,\n expiresIn: tokens.expires_in || 3600,\n idToken: tokens.id_token,\n issuedAt: new Date(),\n refreshExpiresIn: tokens.refresh_expires_in,\n refreshToken: tokens.refresh_token || upstreamRefreshToken,\n scope: tokens.scope ? tokens.scope.split(\" \") : [],\n tokenType: tokens.token_type || \"Bearer\",\n };\n }\n\n /**\n * Start periodic cleanup of expired transactions and codes\n */\n private startCleanup(): void {\n this.cleanupInterval = setInterval(() => {\n this.cleanup();\n }, 60000); // Run every minute\n }\n\n /**\n * Validate redirect URI against allowed patterns\n */\n private validateRedirectUri(uri: string): boolean {\n try {\n const url = new URL(uri);\n const patterns = this.config.allowedRedirectUriPatterns || [];\n\n for (const pattern of patterns) {\n if (this.matchesPattern(uri, pattern)) {\n return true;\n }\n }\n\n // Default: allow https and localhost\n return (\n url.protocol === \"https:\" ||\n url.hostname === \"localhost\" ||\n url.hostname === \"127.0.0.1\"\n );\n } catch {\n return false;\n }\n }\n}\n\n/**\n * OAuth Proxy Error\n */\nexport class OAuthProxyError extends Error {\n constructor(\n public code: string,\n public description?: string,\n public statusCode: number = 400,\n ) {\n super(code);\n this.name = \"OAuthProxyError\";\n }\n\n toJSON(): OAuthError {\n return {\n error: this.code,\n error_description: this.description,\n };\n }\n\n toResponse(): Response {\n return new Response(JSON.stringify(this.toJSON()), {\n headers: { \"Content-Type\": \"application/json\" },\n status: this.statusCode,\n });\n }\n}\n","/**\n * OAuth Proxy Types\n * Type definitions for the OAuth 2.1 Proxy implementation\n */\n\n/**\n * Default TTL values for token expiration (in seconds)\n */\nexport const DEFAULT_ACCESS_TOKEN_TTL = 3600; // 1 hour\nexport const DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH = 31536000; // 1 year\nexport const DEFAULT_REFRESH_TOKEN_TTL = 2592000; // 30 days\nexport const DEFAULT_AUTHORIZATION_CODE_TTL = 300; // 5 minutes\nexport const DEFAULT_TRANSACTION_TTL = 600; // 10 minutes\n\n/**\n * OAuth authorization request parameters\n */\nexport interface AuthorizationParams {\n [key: string]: unknown;\n client_id: string;\n code_challenge?: string;\n code_challenge_method?: string;\n redirect_uri: string;\n response_type: string;\n scope?: string;\n state?: string;\n}\n\n/**\n * Authorization code storage with PKCE validation\n */\nexport interface ClientCode {\n /** Client ID that owns this code */\n clientId: string;\n /** Authorization code */\n code: string;\n /** PKCE code challenge for validation */\n codeChallenge: string;\n /** PKCE code challenge method */\n codeChallengeMethod: string;\n /** Code creation timestamp */\n createdAt: Date;\n /** Code expiration timestamp */\n expiresAt: Date;\n /** Associated transaction ID */\n transactionId: string;\n /** Upstream tokens obtained from provider */\n upstreamTokens: UpstreamTokenSet;\n /** Whether code has been used */\n used?: boolean;\n}\n\n/**\n * Consent data for user approval\n */\nexport interface ConsentData {\n clientName: string;\n provider: string;\n scope: string[];\n timestamp: number;\n transactionId: string;\n}\n\n/**\n * Custom claims passthrough configuration\n */\nexport interface CustomClaimsPassthroughConfig {\n /** Allow nested objects/arrays in claims. Default: false (only primitives) */\n allowComplexClaims?: boolean;\n\n /** Only passthrough these specific claims (allowlist). Default: undefined (allow all non-protected) */\n allowedClaims?: string[];\n\n /** Never passthrough these claims (blocklist, in addition to protected claims). Default: [] */\n blockedClaims?: string[];\n\n /** Prefix upstream claims to prevent collisions. Default: false (no prefix) */\n claimPrefix?: false | string;\n\n /** Enable passthrough from upstream access token (if JWT format). Default: true */\n fromAccessToken?: boolean;\n\n /** Enable passthrough from upstream ID token. Default: true */\n fromIdToken?: boolean;\n\n /** Maximum length for claim values. Default: 2000 */\n maxClaimValueSize?: number;\n}\n\n/**\n * Client metadata for storage\n */\nexport interface DCRClientMetadata {\n client_name?: string;\n client_uri?: string;\n contacts?: string[];\n jwks?: Record<string, unknown>;\n jwks_uri?: string;\n logo_uri?: string;\n policy_uri?: string;\n scope?: string;\n software_id?: string;\n software_version?: string;\n tos_uri?: string;\n}\n\n/**\n * RFC 7591 Dynamic Client Registration Request\n */\nexport interface DCRRequest {\n /** Client name */\n client_name?: string;\n /** Client homepage URL */\n client_uri?: string;\n /** Contact email addresses */\n contacts?: string[];\n /** Allowed grant types */\n grant_types?: string[];\n /** JWKS object */\n jwks?: Record<string, unknown>;\n /** JWKS URI */\n jwks_uri?: string;\n /** Client logo URL */\n logo_uri?: string;\n /** Privacy policy URL */\n policy_uri?: string;\n /** REQUIRED: Array of redirect URIs */\n redirect_uris: string[];\n /** Allowed response types */\n response_types?: string[];\n /** Requested scope */\n scope?: string;\n /** Software identifier */\n software_id?: string;\n /** Software version */\n software_version?: string;\n /** Token endpoint authentication method */\n token_endpoint_auth_method?: string;\n /** Terms of service URL */\n tos_uri?: string;\n}\n\n/**\n * RFC 7591 Dynamic Client Registration Response\n */\nexport interface DCRResponse {\n /** REQUIRED: Client identifier */\n client_id: string;\n /** Client ID issued timestamp */\n client_id_issued_at?: number;\n client_name?: string;\n /** Client secret */\n client_secret?: string;\n /** Client secret expiration (0 = never) */\n client_secret_expires_at?: number;\n client_uri?: string;\n contacts?: string[];\n grant_types?: string[];\n jwks?: Record<string, unknown>;\n jwks_uri?: string;\n logo_uri?: string;\n policy_uri?: string;\n /** Echo back all registered metadata */\n redirect_uris: string[];\n /** Registration access token */\n registration_access_token?: string;\n /** Registration client URI */\n registration_client_uri?: string;\n response_types?: string[];\n scope?: string;\n software_id?: string;\n software_version?: string;\n token_endpoint_auth_method?: string;\n tos_uri?: string;\n}\n\n/**\n * OAuth error response\n */\nexport interface OAuthError {\n error: string;\n error_description?: string;\n error_uri?: string;\n}\n\n/**\n * OAuth Proxy provider for pre-configured providers\n */\nexport interface OAuthProviderConfig {\n baseUrl: string;\n clientId: string;\n clientSecret: string;\n consentRequired?: boolean;\n scopes?: string[];\n}\n\n/**\n * Configuration for the OAuth Proxy\n */\nexport interface OAuthProxyConfig {\n /** Access token TTL in seconds (default: 3600) */\n accessTokenTtl?: number;\n /** Allowed redirect URI patterns for client registration */\n allowedRedirectUriPatterns?: string[];\n /** Authorization code TTL in seconds (default: 300) */\n authorizationCodeTtl?: number;\n /** Base URL of this proxy server */\n baseUrl: string;\n /** Require user consent (default: true) */\n consentRequired?: boolean;\n /** Secret key for signing consent cookies */\n consentSigningKey?: string;\n /**\n * Custom claims passthrough configuration.\n * When enabled (default), extracts custom claims from upstream access token and ID token\n * and includes them in the proxy's issued JWT tokens.\n * This enables authorization based on upstream roles, permissions, etc.\n * Set to false to disable claims passthrough entirely.\n * Default: true (enabled with default settings)\n */\n customClaimsPassthrough?: boolean | CustomClaimsPassthroughConfig;\n /** Enable token swap pattern (default: true) - issues short-lived JWTs instead of passing through upstream tokens */\n enableTokenSwap?: boolean;\n /** Encryption key for token storage (default: auto-generated). Set to false to disable encryption. */\n encryptionKey?: false | string;\n /** Forward client's PKCE to upstream (default: false) */\n forwardPkce?: boolean;\n /** Secret key for signing JWTs when token swap is enabled */\n jwtSigningKey?: string;\n /** OAuth callback path (default: /oauth/callback) */\n redirectPath?: string;\n /** Refresh token TTL in seconds (default: 2592000) */\n refreshTokenTtl?: number;\n /** Scopes to request from upstream provider */\n scopes?: string[];\n /** Custom token storage backend */\n tokenStorage?: TokenStorage;\n /** Custom token verifier for validating upstream tokens */\n tokenVerifier?: TokenVerifier;\n /** Transaction TTL in seconds (default: 600) */\n transactionTtl?: number;\n /** Upstream provider's authorization endpoint URL */\n upstreamAuthorizationEndpoint: string;\n /** Pre-registered client ID with upstream provider */\n upstreamClientId: string;\n /** Pre-registered client secret with upstream provider */\n upstreamClientSecret: string;\n /** Upstream provider's token endpoint URL */\n upstreamTokenEndpoint: string;\n /** Upstream token endpoint authentication method (default: \"client_secret_basic\") */\n upstreamTokenEndpointAuthMethod?:\n | \"client_secret_basic\"\n | \"client_secret_post\";\n}\n\n/**\n * OAuth transaction tracking active authorization flows\n */\nexport interface OAuthTransaction {\n /** Client's callback URL */\n clientCallbackUrl: string;\n /** Client's PKCE code challenge */\n clientCodeChallenge: string;\n /** Client's PKCE code challenge method (S256 or plain) */\n clientCodeChallengeMethod: string;\n /** Client ID from registration */\n clientId: string;\n /** Whether user consent was given */\n consentGiven?: boolean;\n /** Transaction creation timestamp */\n createdAt: Date;\n /** Transaction expiration timestamp */\n expiresAt: Date;\n /** Unique transaction ID */\n id: string;\n /** Additional state data */\n metadata?: Record<string, unknown>;\n /** Proxy-generated PKCE challenge for upstream */\n proxyCodeChallenge: string;\n /** Proxy-generated PKCE verifier for upstream */\n proxyCodeVerifier: string;\n /** Requested scopes */\n scope: string[];\n /** OAuth state parameter */\n state: string;\n}\n\n/**\n * PKCE pair\n */\nexport interface PKCEPair {\n challenge: string;\n verifier: string;\n}\n\n/**\n * Dynamic client registration data\n */\nexport interface ProxyDCRClient {\n /** Registered callback URL */\n callbackUrl: string;\n /** Generated or assigned client ID */\n clientId: string;\n /** Client secret (optional) */\n clientSecret?: string;\n /** Client metadata from registration request */\n metadata?: DCRClientMetadata;\n /** Client registration timestamp */\n registeredAt: Date;\n}\n\n/**\n * OAuth refresh token request\n */\nexport interface RefreshRequest {\n client_id: string;\n client_secret?: string;\n grant_type: \"refresh_token\";\n refresh_token: string;\n scope?: string;\n}\n\n/**\n * Token mapping for JWT swap pattern\n * Maps JTI to upstream token reference\n */\nexport interface TokenMapping {\n /** Client ID */\n clientId: string;\n /** Creation timestamp */\n createdAt: Date;\n /** Expiration timestamp */\n expiresAt: Date;\n /** JTI from FastMCP JWT */\n jti: string;\n /** Scopes */\n scope: string[];\n /** Reference to upstream token set */\n upstreamTokenKey: string;\n}\n\n/**\n * OAuth token request\n */\nexport interface TokenRequest {\n client_id: string;\n client_secret?: string;\n code: string;\n code_verifier?: string;\n grant_type: \"authorization_code\";\n redirect_uri: string;\n}\n\n/**\n * OAuth token response\n */\nexport interface TokenResponse {\n access_token: string;\n expires_in: number;\n id_token?: string;\n refresh_token?: string;\n scope?: string;\n token_type: string;\n}\n\n/**\n * Token storage interface\n */\nexport interface TokenStorage {\n /** Clean up expired entries */\n cleanup(): Promise<void>;\n /** Delete a value */\n delete(key: string): Promise<void>;\n /** Retrieve a value */\n get(key: string): Promise<null | unknown>;\n /** Save a value with optional TTL */\n save(key: string, value: unknown, ttl?: number): Promise<void>;\n}\n\n/**\n * Token verification result\n */\nexport interface TokenVerificationResult {\n claims?: Record<string, unknown>;\n error?: string;\n valid: boolean;\n}\n\n/**\n * Token verifier for validating upstream tokens\n */\nexport interface TokenVerifier {\n verify(token: string): Promise<TokenVerificationResult>;\n}\n\n/**\n * Token set from upstream OAuth provider\n */\nexport interface UpstreamTokenSet {\n /** Access token */\n accessToken: string;\n /** Token expiration in seconds */\n expiresIn: number;\n /** ID token (for OIDC) */\n idToken?: string;\n /** Token issuance timestamp */\n issuedAt: Date;\n /** Refresh token expiration in seconds (if provided by upstream) */\n refreshExpiresIn?: number;\n /** Refresh token (if provided) */\n refreshToken?: string;\n /** Granted scopes */\n scope: string[];\n /** Token type (usually \"Bearer\") */\n tokenType: string;\n}\n","/**\n * ClaimsExtractor\n * Securely extracts and filters custom claims from upstream OAuth tokens\n */\n\nimport type { CustomClaimsPassthroughConfig } from \"../types.js\";\n\nexport class ClaimsExtractor {\n private config: CustomClaimsPassthroughConfig;\n\n // Claims that MUST NOT be copied from upstream (protect proxy's JWT integrity)\n private readonly PROTECTED_CLAIMS = new Set([\n \"aud\",\n \"client_id\",\n \"exp\",\n \"iat\",\n \"iss\",\n \"jti\",\n \"nbf\",\n ]);\n\n constructor(config: boolean | CustomClaimsPassthroughConfig) {\n // Handle boolean shorthand: true = default config, false = disabled\n if (typeof config === \"boolean\") {\n config = config ? {} : { fromAccessToken: false, fromIdToken: false };\n }\n\n // Apply defaults\n this.config = {\n allowComplexClaims: config.allowComplexClaims || false,\n allowedClaims: config.allowedClaims,\n blockedClaims: config.blockedClaims || [],\n claimPrefix:\n config.claimPrefix !== undefined ? config.claimPrefix : false, // Default: no prefix\n fromAccessToken: config.fromAccessToken !== false, // Default: true\n fromIdToken: config.fromIdToken !== false, // Default: true\n maxClaimValueSize: config.maxClaimValueSize || 2000,\n };\n }\n\n /**\n * Extract claims from a token (access token or ID token)\n */\n async extract(\n token: string,\n tokenType: \"access\" | \"id\",\n ): Promise<null | Record<string, unknown>> {\n // Check if this token type is enabled\n if (tokenType === \"access\" && !this.config.fromAccessToken) {\n return null;\n }\n if (tokenType === \"id\" && !this.config.fromIdToken) {\n return null;\n }\n\n // Detect if token is JWT format (3 parts separated by dots)\n if (!this.isJWT(token)) {\n // Opaque token - no claims to extract\n return null;\n }\n\n // Decode JWT payload (base64url decode only, no signature verification)\n // We trust the token because it came from upstream via server-to-server exchange\n const payload = this.decodeJWTPayload(token);\n if (!payload) {\n return null;\n }\n\n // Filter and validate claims\n const filtered = this.filterClaims(payload);\n\n // Apply prefix if configured\n return this.applyPrefix(filtered);\n }\n\n /**\n * Apply prefix to claim names (if configured)\n */\n private applyPrefix(\n claims: Record<string, unknown>,\n ): Record<string, unknown> {\n const prefix = this.config.claimPrefix;\n\n // No prefix configured or explicitly disabled\n if (prefix === false || prefix === \"\" || prefix === undefined) {\n return claims;\n }\n\n // Apply prefix to all claim names\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(claims)) {\n result[`${prefix}${key}`] = value;\n }\n\n return result;\n }\n\n /**\n * Decode JWT payload without signature verification\n * Safe because token came from trusted upstream via server-to-server exchange\n */\n private decodeJWTPayload(token: string): null | Record<string, unknown> {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n return null;\n }\n\n // Decode the payload (middle part)\n const payload = Buffer.from(parts[1], \"base64url\").toString(\"utf-8\");\n return JSON.parse(payload) as Record<string, unknown>;\n } catch (error) {\n // Invalid JWT format or JSON\n console.warn(`Failed to decode JWT payload: ${error}`);\n return null;\n }\n }\n\n /**\n * Filter claims based on security rules\n */\n private filterClaims(\n claims: Record<string, unknown>,\n ): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(claims)) {\n // RULE 1: Skip protected claims (ALWAYS enforced)\n if (this.PROTECTED_CLAIMS.has(key)) {\n continue;\n }\n\n // RULE 2: Skip blocked claims\n if (this.config.blockedClaims?.includes(key)) {\n continue;\n }\n\n // RULE 3: If allowlist exists, only include allowed claims\n if (\n this.config.allowedClaims &&\n !this.config.allowedClaims.includes(key)\n ) {\n continue;\n }\n\n // RULE 4: Validate claim value\n if (!this.isValidClaimValue(value)) {\n console.warn(`Skipping claim '${key}' due to invalid value`);\n continue;\n }\n\n result[key] = value;\n }\n\n return result;\n }\n\n /**\n * Check if a token is in JWT format\n */\n private isJWT(token: string): boolean {\n return token.split(\".\").length === 3;\n }\n\n /**\n * Validate a claim value (type and size checks)\n */\n private isValidClaimValue(value: unknown): boolean {\n if (value === null || value === undefined) {\n return false;\n }\n\n const type = typeof value;\n\n // Primitive types (string, number, boolean) are always allowed\n if (type === \"string\") {\n const maxSize = this.config.maxClaimValueSize ?? 2000;\n return (value as string).length <= maxSize;\n }\n\n if (type === \"number\" || type === \"boolean\") {\n return true;\n }\n\n // Arrays and objects only if explicitly allowed\n if (Array.isArray(value) || type === \"object\") {\n // Complex types not allowed by default (security)\n if (!this.config.allowComplexClaims) {\n return false;\n }\n\n // Check serialized size\n try {\n const stringified = JSON.stringify(value);\n const maxSize = this.config.maxClaimValueSize ?? 2000;\n return stringified.length <= maxSize;\n } catch {\n // Can't serialize - reject\n return false;\n }\n }\n\n // Unknown type - reject\n return false;\n }\n}\n","/**\n * Consent Management\n * Handles user consent flow for OAuth authorization\n */\n\nimport { createHmac } from \"crypto\";\n\nimport type { ConsentData, OAuthTransaction } from \"../types.js\";\n\n/**\n * Manages consent screens and cookie signing\n */\nexport class ConsentManager {\n private signingKey: string;\n\n constructor(signingKey: string) {\n this.signingKey = signingKey || this.generateDefaultKey();\n }\n\n /**\n * Create HTTP response with consent screen\n */\n createConsentResponse(\n transaction: OAuthTransaction,\n provider: string,\n ): Response {\n const consentData: ConsentData = {\n clientName: \"MCP Client\",\n provider,\n scope: transaction.scope,\n timestamp: Date.now(),\n transactionId: transaction.id,\n };\n\n const html = this.generateConsentScreen(consentData);\n\n return new Response(html, {\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n },\n status: 200,\n });\n }\n\n /**\n * Generate HTML for consent screen\n */\n generateConsentScreen(data: ConsentData): string {\n const { clientName, provider, scope, transactionId } = data;\n\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Authorization Request</title>\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n min-height: 100vh;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 20px;\n }\n\n .consent-container {\n background: white;\n border-radius: 12px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n max-width: 480px;\n width: 100%;\n padding: 40px;\n }\n\n .header {\n text-align: center;\n margin-bottom: 30px;\n }\n\n .header h1 {\n color: #1a202c;\n font-size: 24px;\n margin-bottom: 8px;\n }\n\n .header p {\n color: #718096;\n font-size: 14px;\n }\n\n .app-info {\n background: #f7fafc;\n border-radius: 8px;\n padding: 20px;\n margin-bottom: 24px;\n }\n\n .app-info h2 {\n color: #2d3748;\n font-size: 18px;\n margin-bottom: 12px;\n }\n\n .app-name {\n color: #667eea;\n font-weight: 600;\n }\n\n .permissions {\n margin-top: 16px;\n }\n\n .permissions h3 {\n color: #4a5568;\n font-size: 14px;\n margin-bottom: 8px;\n font-weight: 600;\n }\n\n .permissions ul {\n list-style: none;\n }\n\n .permissions li {\n color: #718096;\n font-size: 14px;\n padding: 6px 0;\n padding-left: 24px;\n position: relative;\n }\n\n .permissions li:before {\n content: \"✓\";\n position: absolute;\n left: 0;\n color: #48bb78;\n font-weight: bold;\n }\n\n .warning {\n background: #fffaf0;\n border-left: 4px solid #ed8936;\n padding: 12px 16px;\n margin-bottom: 24px;\n border-radius: 4px;\n }\n\n .warning p {\n color: #744210;\n font-size: 13px;\n line-height: 1.5;\n }\n\n .actions {\n display: flex;\n gap: 12px;\n }\n\n button {\n flex: 1;\n padding: 14px 24px;\n border: none;\n border-radius: 6px;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .approve {\n background: #667eea;\n color: white;\n }\n\n .approve:hover {\n background: #5a67d8;\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);\n }\n\n .deny {\n background: #e2e8f0;\n color: #4a5568;\n }\n\n .deny:hover {\n background: #cbd5e0;\n }\n\n .footer {\n margin-top: 24px;\n text-align: center;\n color: #a0aec0;\n font-size: 12px;\n }\n </style>\n</head>\n<body>\n <div class=\"consent-container\">\n <div class=\"header\">\n <h1>🔐 Authorization Request</h1>\n <p>via ${this.escapeHtml(provider)}</p>\n </div>\n\n <div class=\"app-info\">\n <h2>\n <span class=\"app-name\">${this.escapeHtml(clientName || \"An application\")}</span>\n requests access\n </h2>\n\n <div class=\"permissions\">\n <h3>This will allow the app to:</h3>\n <ul>\n ${scope.map((s) => `<li>${this.escapeHtml(this.formatScope(s))}</li>`).join(\"\")}\n </ul>\n </div>\n </div>\n\n <div class=\"warning\">\n <p>\n <strong>⚠️ Important:</strong> Only approve if you trust this application.\n By approving, you authorize it to access your account information.\n </p>\n </div>\n\n <form method=\"POST\" action=\"/oauth/consent\">\n <input type=\"hidden\" name=\"transaction_id\" value=\"${this.escapeHtml(transactionId)}\">\n <div class=\"actions\">\n <button type=\"submit\" name=\"action\" value=\"deny\" class=\"deny\">\n Deny\n </button>\n <button type=\"submit\" name=\"action\" value=\"approve\" class=\"approve\">\n Approve\n </button>\n </div>\n </form>\n\n <div class=\"footer\">\n <p>This consent is required to prevent unauthorized access.</p>\n </div>\n </div>\n</body>\n</html>\n `.trim();\n }\n\n /**\n * Sign consent data for cookie\n */\n signConsentCookie(data: ConsentData): string {\n const payload = JSON.stringify(data);\n const signature = this.sign(payload);\n\n return `${Buffer.from(payload).toString(\"base64\")}.${signature}`;\n }\n\n /**\n * Validate and parse consent cookie\n */\n validateConsentCookie(cookie: string): ConsentData | null {\n try {\n const [payloadB64, signature] = cookie.split(\".\");\n\n if (!payloadB64 || !signature) {\n return null;\n }\n\n const payload = Buffer.from(payloadB64, \"base64\").toString(\"utf8\");\n const expectedSignature = this.sign(payload);\n\n if (signature !== expectedSignature) {\n return null;\n }\n\n const data = JSON.parse(payload) as ConsentData;\n\n // Check if consent is still valid (5 minutes)\n const age = Date.now() - data.timestamp;\n if (age > 5 * 60 * 1000) {\n return null;\n }\n\n return data;\n } catch {\n return null;\n }\n }\n\n /**\n * Escape HTML to prevent XSS\n */\n private escapeHtml(text: string): string {\n const map: Record<string, string> = {\n \"'\": \"&#x27;\",\n '\"': \"&quot;\",\n \"/\": \"&#x2F;\",\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n };\n\n return text.replace(/[&<>\"'/]/g, (char) => map[char] || char);\n }\n\n /**\n * Format scope for display\n */\n private formatScope(scope: string): string {\n // Convert scope names to readable format\n const scopeMap: Record<string, string> = {\n email: \"Access your email address\",\n openid: \"Verify your identity\",\n profile: \"View your basic profile information\",\n \"read:user\": \"Read your user information\",\n \"write:user\": \"Modify your user information\",\n };\n\n return scopeMap[scope] || scope.replace(/_/g, \" \").replace(/:/g, \" - \");\n }\n\n /**\n * Generate default signing key if none provided\n */\n private generateDefaultKey(): string {\n return `fastmcp-consent-${Date.now()}-${Math.random()}`;\n }\n\n /**\n * Sign a payload using HMAC-SHA256\n */\n private sign(payload: string): string {\n return createHmac(\"sha256\", this.signingKey).update(payload).digest(\"hex\");\n }\n}\n","/**\n * JWT Issuer for OAuth Proxy\n * Issues and validates short-lived JWTs that reference upstream provider tokens\n */\n\nimport { createHmac, pbkdf2, randomBytes } from \"crypto\";\nimport { promisify } from \"util\";\n\nimport {\n DEFAULT_ACCESS_TOKEN_TTL,\n DEFAULT_REFRESH_TOKEN_TTL,\n} from \"../types.js\";\n\nconst pbkdf2Async = promisify(pbkdf2);\n\n/**\n * JWT Claims for FastMCP tokens\n */\nexport interface JWTClaims {\n /** Additional custom claims from upstream tokens */\n [key: string]: unknown;\n /** Audience */\n aud: string;\n /** Client ID */\n client_id: string;\n /** Expiration time (seconds since epoch) */\n exp: number;\n /** Issued at time (seconds since epoch) */\n iat: number;\n /** Issuer */\n iss: string;\n /** JWT ID (unique identifier) */\n jti: string;\n /** Scopes */\n scope: string[];\n}\n\n/**\n * JWT Issuer configuration\n */\nexport interface JWTIssuerConfig {\n /** Token expiration in seconds (default: 3600 = 1 hour) */\n accessTokenTtl?: number;\n /** Audience for issued tokens */\n audience: string;\n /** Issuer identifier */\n issuer: string;\n /** Refresh token expiration in seconds (default: 2592000 = 30 days) */\n refreshTokenTtl?: number;\n /** Secret key for signing tokens */\n signingKey: string;\n}\n\n/**\n * Token validation result\n */\nexport interface TokenValidationResult {\n /** Decoded claims if valid */\n claims?: JWTClaims;\n /** Error message if invalid */\n error?: string;\n /** Whether token is valid */\n valid: boolean;\n}\n\n/**\n * JWT Header\n */\ninterface JWTHeader {\n alg: string;\n typ: string;\n}\n\n/**\n * JWT Issuer\n * Issues and validates HS256-signed JWTs for the OAuth proxy\n */\nexport class JWTIssuer {\n private accessTokenTtl: number;\n private audience: string;\n private issuer: string;\n private refreshTokenTtl: number;\n private signingKey: Buffer;\n\n constructor(config: JWTIssuerConfig) {\n this.issuer = config.issuer;\n this.audience = config.audience;\n this.accessTokenTtl = config.accessTokenTtl || DEFAULT_ACCESS_TOKEN_TTL;\n this.refreshTokenTtl = config.refreshTokenTtl || DEFAULT_REFRESH_TOKEN_TTL;\n this.signingKey = Buffer.from(config.signingKey);\n }\n\n /**\n * Derive a signing key from a secret\n * Uses PBKDF2 for key derivation\n */\n static async deriveKey(\n secret: string,\n iterations: number = 100000,\n ): Promise<string> {\n const salt = Buffer.from(\"fastmcp-oauth-proxy\");\n const key = await pbkdf2Async(secret, salt, iterations, 32, \"sha256\");\n return key.toString(\"base64\");\n }\n\n /**\n * Issue an access token\n */\n issueAccessToken(\n clientId: string,\n scope: string[],\n additionalClaims?: Record<string, unknown>,\n expiresIn?: number,\n ): string {\n const now = Math.floor(Date.now() / 1000);\n const jti = this.generateJti();\n\n const claims: JWTClaims = {\n aud: this.audience,\n client_id: clientId,\n exp: now + (expiresIn ?? this.accessTokenTtl),\n iat: now,\n iss: this.issuer,\n jti,\n scope,\n // Merge additional claims (custom claims from upstream)\n ...(additionalClaims || {}),\n };\n\n return this.signToken(claims);\n }\n\n /**\n * Issue a refresh token\n */\n issueRefreshToken(\n clientId: string,\n scope: string[],\n additionalClaims?: Record<string, unknown>,\n expiresIn?: number,\n ): string {\n const now = Math.floor(Date.now() / 1000);\n const jti = this.generateJti();\n\n const claims: JWTClaims = {\n aud: this.audience,\n client_id: clientId,\n exp: now + (expiresIn ?? this.refreshTokenTtl),\n iat: now,\n iss: this.issuer,\n jti,\n scope,\n // Merge additional claims (custom claims from upstream)\n ...(additionalClaims || {}),\n };\n\n return this.signToken(claims);\n }\n\n /**\n * Validate a JWT token\n */\n async verify(token: string): Promise<TokenValidationResult> {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n return {\n error: \"Invalid token format\",\n valid: false,\n };\n }\n\n const [headerB64, payloadB64, signatureB64] = parts;\n\n // Verify signature\n const expectedSignature = this.sign(`${headerB64}.${payloadB64}`);\n if (signatureB64 !== expectedSignature) {\n return {\n error: \"Invalid signature\",\n valid: false,\n };\n }\n\n // Decode claims\n const claims: JWTClaims = JSON.parse(\n Buffer.from(payloadB64, \"base64url\").toString(\"utf-8\"),\n );\n\n // Validate claims\n const now = Math.floor(Date.now() / 1000);\n\n if (claims.exp <= now) {\n return {\n claims,\n error: \"Token expired\",\n valid: false,\n };\n }\n\n if (claims.iss !== this.issuer) {\n return {\n claims,\n error: \"Invalid issuer\",\n valid: false,\n };\n }\n\n if (claims.aud !== this.audience) {\n return {\n claims,\n error: \"Invalid audience\",\n valid: false,\n };\n }\n\n return {\n claims,\n valid: true,\n };\n } catch (error) {\n return {\n error: error instanceof Error ? error.message : \"Validation failed\",\n valid: false,\n };\n }\n }\n\n /**\n * Generate unique JWT ID\n */\n private generateJti(): string {\n return randomBytes(16).toString(\"base64url\");\n }\n\n /**\n * Sign data with HMAC-SHA256\n */\n private sign(data: string): string {\n const hmac = createHmac(\"sha256\", this.signingKey);\n hmac.update(data);\n return hmac.digest(\"base64url\");\n }\n\n /**\n * Sign a JWT token\n */\n private signToken(claims: JWTClaims): string {\n const header: JWTHeader = {\n alg: \"HS256\",\n typ: \"JWT\",\n };\n\n const headerB64 = Buffer.from(JSON.stringify(header)).toString(\"base64url\");\n const payloadB64 = Buffer.from(JSON.stringify(claims)).toString(\n \"base64url\",\n );\n\n const signature = this.sign(`${headerB64}.${payloadB64}`);\n\n return `${headerB64}.${payloadB64}.${signature}`;\n }\n}\n","/**\n * PKCE (Proof Key for Code Exchange) Utilities\n * Implements RFC 7636 for OAuth 2.0 public clients\n */\n\nimport { createHash, randomBytes } from \"crypto\";\n\nimport type { PKCEPair } from \"../types.js\";\n\n/**\n * PKCE utility class for generating and validating code challenges\n */\nexport class PKCEUtils {\n /**\n * Generate a code challenge from a verifier\n * @param verifier The code verifier\n * @param method Challenge method: 'S256' or 'plain' (default: 'S256')\n * @returns Base64URL-encoded challenge string\n */\n static generateChallenge(\n verifier: string,\n method: \"plain\" | \"S256\" = \"S256\",\n ): string {\n if (method === \"plain\") {\n return verifier;\n }\n\n if (method === \"S256\") {\n const hash = createHash(\"sha256\");\n hash.update(verifier);\n return PKCEUtils.base64URLEncode(hash.digest());\n }\n\n throw new Error(`Unsupported challenge method: ${method}`);\n }\n\n /**\n * Generate a complete PKCE pair (verifier + challenge)\n * @param method Challenge method: 'S256' or 'plain' (default: 'S256')\n * @returns Object containing verifier and challenge\n */\n static generatePair(method: \"plain\" | \"S256\" = \"S256\"): PKCEPair {\n const verifier = PKCEUtils.generateVerifier();\n const challenge = PKCEUtils.generateChallenge(verifier, method);\n\n return {\n challenge,\n verifier,\n };\n }\n\n /**\n * Generate a cryptographically secure code verifier\n * @param length Length of verifier (43-128 characters, default: 128)\n * @returns Base64URL-encoded verifier string\n */\n static generateVerifier(length: number = 128): string {\n if (length < 43 || length > 128) {\n throw new Error(\"PKCE verifier length must be between 43 and 128\");\n }\n\n // Generate random bytes and encode as base64url\n // Need more bytes because base64 encoding expands data\n const byteLength = Math.ceil((length * 3) / 4);\n const randomBytesBuffer = randomBytes(byteLength);\n\n return PKCEUtils.base64URLEncode(randomBytesBuffer).slice(0, length);\n }\n\n /**\n * Validate a code verifier against a challenge\n * @param verifier The code verifier to validate\n * @param challenge The expected challenge\n * @param method The challenge method used\n * @returns True if verifier matches challenge\n */\n static validateChallenge(\n verifier: string,\n challenge: string,\n method: string,\n ): boolean {\n if (!verifier || !challenge) {\n return false;\n }\n\n if (method === \"plain\") {\n return verifier === challenge;\n }\n\n if (method === \"S256\") {\n const computedChallenge = PKCEUtils.generateChallenge(verifier, \"S256\");\n return computedChallenge === challenge;\n }\n\n // Unknown method\n return false;\n }\n\n /**\n * Encode a buffer as base64url (RFC 4648)\n * @param buffer Buffer to encode\n * @returns Base64URL-encoded string\n */\n private static base64URLEncode(buffer: Buffer): string {\n return buffer\n .toString(\"base64\")\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=/g, \"\");\n }\n}\n","/**\n * Token Storage Implementations\n * Secure storage for OAuth tokens and transaction state\n */\n\nimport {\n createCipheriv,\n createDecipheriv,\n randomBytes,\n scryptSync,\n} from \"crypto\";\n\nimport type { TokenStorage } from \"../types.js\";\n\ninterface StorageEntry {\n expiresAt: number;\n value: unknown;\n}\n\n/**\n * Encrypted token storage wrapper\n * Encrypts values using AES-256-GCM before storing\n */\nexport class EncryptedTokenStorage implements TokenStorage {\n private algorithm = \"aes-256-gcm\";\n private backend: TokenStorage;\n private encryptionKey: Buffer;\n\n constructor(backend: TokenStorage, encryptionKey: string) {\n this.backend = backend;\n // Synchronously derive key using scrypt\n const salt = Buffer.from(\"fastmcp-oauth-proxy-salt\");\n this.encryptionKey = scryptSync(encryptionKey, salt, 32);\n }\n\n async cleanup(): Promise<void> {\n await this.backend.cleanup();\n }\n\n async delete(key: string): Promise<void> {\n await this.backend.delete(key);\n }\n\n async get(key: string): Promise<null | unknown> {\n const encrypted = await this.backend.get(key);\n\n if (!encrypted) {\n return null;\n }\n\n try {\n const decrypted = await this.decrypt(\n encrypted as string,\n this.encryptionKey,\n );\n return JSON.parse(decrypted);\n } catch (error) {\n console.error(\"Failed to decrypt value:\", error);\n return null;\n }\n }\n\n async save(key: string, value: unknown, ttl?: number): Promise<void> {\n const encrypted = await this.encrypt(\n JSON.stringify(value),\n this.encryptionKey,\n );\n await this.backend.save(key, encrypted, ttl);\n }\n\n private async decrypt(ciphertext: string, key: Buffer): Promise<string> {\n const parts = ciphertext.split(\":\");\n if (parts.length !== 3) {\n throw new Error(\"Invalid encrypted data format\");\n }\n\n const [ivHex, authTagHex, encrypted] = parts;\n const iv = Buffer.from(ivHex, \"hex\");\n const authTag = Buffer.from(authTagHex, \"hex\");\n\n const decipher = createDecipheriv(this.algorithm, key, iv);\n // Use type assertion for GCM-specific method\n (decipher as unknown as { setAuthTag(buffer: Buffer): void }).setAuthTag(\n authTag,\n );\n\n let decrypted = decipher.update(encrypted, \"hex\", \"utf8\");\n decrypted += decipher.final(\"utf8\");\n\n return decrypted;\n }\n\n private async encrypt(plaintext: string, key: Buffer): Promise<string> {\n const iv = randomBytes(16);\n const cipher = createCipheriv(this.algorithm, key, iv);\n\n let encrypted = cipher.update(plaintext, \"utf8\", \"hex\");\n encrypted += cipher.final(\"hex\");\n\n // Use type assertion for GCM-specific method\n const authTag = (\n cipher as unknown as { getAuthTag(): Buffer }\n ).getAuthTag();\n\n // Return format: iv:authTag:encrypted\n return `${iv.toString(\"hex\")}:${authTag.toString(\"hex\")}:${encrypted}`;\n }\n}\n\n/**\n * In-memory token storage with TTL support\n */\nexport class MemoryTokenStorage implements TokenStorage {\n private cleanupInterval: NodeJS.Timeout | null = null;\n private store: Map<string, StorageEntry> = new Map();\n\n constructor(cleanupIntervalMs: number = 60000) {\n // Run cleanup every minute by default\n this.cleanupInterval = setInterval(\n () => void this.cleanup(),\n cleanupIntervalMs,\n );\n }\n\n async cleanup(): Promise<void> {\n const now = Date.now();\n const keysToDelete: string[] = [];\n\n for (const [key, entry] of this.store.entries()) {\n if (entry.expiresAt < now) {\n keysToDelete.push(key);\n }\n }\n\n for (const key of keysToDelete) {\n this.store.delete(key);\n }\n }\n\n async delete(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n /**\n * Destroy the storage and clear cleanup interval\n */\n destroy(): void {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = null;\n }\n this.store.clear();\n }\n\n async get(key: string): Promise<null | unknown> {\n const entry = this.store.get(key);\n\n if (!entry) {\n return null;\n }\n\n if (entry.expiresAt < Date.now()) {\n this.store.delete(key);\n return null;\n }\n\n return entry.value;\n }\n\n async save(key: string, value: unknown, ttl?: number): Promise<void> {\n const expiresAt = ttl ? Date.now() + ttl * 1000 : Number.MAX_SAFE_INTEGER;\n\n this.store.set(key, {\n expiresAt,\n value,\n });\n }\n\n /**\n * Get the number of stored items\n */\n size(): number {\n return this.store.size;\n }\n}\n","/**\n * AuthProvider Base Class\n * High-level abstraction for OAuth authentication that simplifies configuration\n */\n\nimport type { IncomingMessage } from \"node:http\";\n\nimport type { TokenStorage, UpstreamTokenSet } from \"../types.js\";\n\nimport { OAuthProxy } from \"../OAuthProxy.js\";\n\n/**\n * Configuration common to all OAuth providers.\n */\nexport interface AuthProviderConfig {\n /** Allowed redirect URI patterns (default: [\"http://localhost:*\", \"https://*\"]) */\n allowedRedirectUriPatterns?: string[];\n /** Base URL where the MCP server is accessible */\n baseUrl: string;\n /** OAuth client ID */\n clientId: string;\n /** OAuth client secret */\n clientSecret: string;\n /** Require user consent screen (default: true) */\n consentRequired?: boolean;\n /** Encryption key for token storage (auto-generated if not provided, set to false to disable) */\n encryptionKey?: false | string;\n /** JWT signing key (auto-generated if not provided) */\n jwtSigningKey?: string;\n /** Scopes to request (defaults vary by provider) */\n scopes?: string[];\n /** Token storage backend (default: MemoryTokenStorage) */\n tokenStorage?: TokenStorage;\n}\n\n/**\n * Configuration for generic OAuth provider (user-specified endpoints).\n */\nexport interface GenericOAuthProviderConfig extends AuthProviderConfig {\n /** OAuth authorization endpoint URL */\n authorizationEndpoint: string;\n /** OAuth token endpoint URL */\n tokenEndpoint: string;\n /** Token endpoint auth method (default: \"client_secret_basic\") */\n tokenEndpointAuthMethod?: \"client_secret_basic\" | \"client_secret_post\";\n}\n\n/**\n * Standard session type for OAuth providers.\n * Contains the upstream access token and optional metadata.\n */\nexport interface OAuthSession {\n /** The upstream OAuth access token */\n accessToken: string;\n /** Additional claims extracted from the token (if customClaimsPassthrough enabled) */\n claims?: Record<string, unknown>;\n /** Token expiration time (Unix timestamp in seconds) */\n expiresAt?: number;\n /** ID token from OIDC providers */\n idToken?: string;\n /** Refresh token (if available) */\n refreshToken?: string;\n /** Scopes granted by the OAuth provider */\n scopes?: string[];\n}\n\n/**\n * Abstract base class for OAuth providers.\n * Encapsulates OAuthProxy creation, authenticate function, and oauth config.\n *\n * Subclasses only need to implement the endpoint and default scope methods.\n */\nexport abstract class AuthProvider<\n TSession extends OAuthSession = OAuthSession,\n> {\n protected config: AuthProviderConfig;\n /**\n * Get the proxy, creating it lazily if needed.\n */\n protected get proxy(): OAuthProxy {\n if (!this._proxy) {\n this._proxy = this.createProxy();\n }\n return this._proxy;\n }\n\n private _proxy: OAuthProxy | undefined;\n\n constructor(config: AuthProviderConfig) {\n this.config = config;\n // Note: proxy is created lazily to allow subclass constructors to run first\n }\n\n /**\n * Authenticate function to be used by FastMCP.\n * Extracts Bearer token, validates it, and returns session with upstream access token.\n */\n async authenticate(\n request: IncomingMessage | undefined,\n ): Promise<TSession | undefined> {\n if (!request) {\n // stdio transport - no HTTP authentication\n return undefined;\n }\n\n const authHeader = request.headers?.authorization;\n if (!authHeader || !authHeader.startsWith(\"Bearer \")) {\n return undefined;\n }\n\n const token = authHeader.slice(7);\n const upstreamTokens = await this.proxy.loadUpstreamTokens(token);\n\n if (!upstreamTokens) {\n return undefined;\n }\n\n return this.createSession(upstreamTokens);\n }\n\n /**\n * Get the OAuth configuration object for FastMCP ServerOptions.\n */\n getOAuthConfig(): {\n authorizationServer: ReturnType<\n OAuthProxy[\"getAuthorizationServerMetadata\"]\n >;\n enabled: true;\n protectedResource: {\n authorizationServers: string[];\n resource: string;\n scopesSupported: string[];\n };\n proxy: OAuthProxy;\n } {\n return {\n authorizationServer: this.proxy.getAuthorizationServerMetadata(),\n enabled: true,\n protectedResource: {\n authorizationServers: [this.config.baseUrl],\n resource: this.config.baseUrl,\n scopesSupported: this.config.scopes ?? this.getDefaultScopes(),\n },\n proxy: this.proxy,\n };\n }\n\n /**\n * Get the OAuthProxy instance (for advanced use cases).\n */\n getProxy(): OAuthProxy {\n return this.proxy;\n }\n\n /** Create the underlying OAuthProxy with provider-specific configuration */\n protected abstract createProxy(): OAuthProxy;\n\n /**\n * Create a session object from upstream tokens.\n * Override in subclasses to add provider-specific session data.\n */\n protected createSession(upstreamTokens: UpstreamTokenSet): TSession {\n return {\n accessToken: upstreamTokens.accessToken,\n expiresAt: upstreamTokens.expiresIn\n ? Math.floor(Date.now() / 1000) + upstreamTokens.expiresIn\n : undefined,\n idToken: upstreamTokens.idToken,\n refreshToken: upstreamTokens.refreshToken,\n scopes: upstreamTokens.scope,\n } as TSession;\n }\n\n /** Get the authorization endpoint for this provider */\n protected abstract getAuthorizationEndpoint(): string;\n\n /** Default scopes for this provider */\n protected abstract getDefaultScopes(): string[];\n\n /** Get the token endpoint for this provider */\n protected abstract getTokenEndpoint(): string;\n}\n","/**\n * Microsoft Azure/Entra ID OAuth Provider\n * Pre-configured OAuth provider for Microsoft Identity Platform\n */\n\nimport { OAuthProxy } from \"../OAuthProxy.js\";\nimport {\n AuthProvider,\n type AuthProviderConfig,\n type OAuthSession,\n} from \"./AuthProvider.js\";\n\n/**\n * Azure-specific configuration\n */\nexport interface AzureProviderConfig extends AuthProviderConfig {\n /** Tenant ID or 'common', 'organizations', 'consumers' (default: 'common') */\n tenantId?: string;\n}\n\n/**\n * Azure-specific session with additional user info\n */\nexport interface AzureSession extends OAuthSession {\n upn?: string;\n}\n\n/**\n * Microsoft Azure AD / Entra ID OAuth 2.0 Provider\n * Callback URL: {baseUrl}/oauth/callback\n */\nexport class AzureProvider extends AuthProvider<AzureSession> {\n private tenantId: string;\n\n constructor(config: AzureProviderConfig) {\n super(config);\n this.tenantId = config.tenantId ?? \"common\";\n }\n\n protected createProxy(): OAuthProxy {\n return new OAuthProxy({\n allowedRedirectUriPatterns: this.config.allowedRedirectUriPatterns ?? [\n \"http://localhost:*\",\n \"https://*\",\n ],\n baseUrl: this.config.baseUrl,\n consentRequired: this.config.consentRequired ?? true,\n encryptionKey: this.config.encryptionKey,\n jwtSigningKey: this.config.jwtSigningKey,\n scopes: this.config.scopes ?? this.getDefaultScopes(),\n tokenStorage: this.config.tokenStorage,\n upstreamAuthorizationEndpoint: this.getAuthorizationEndpoint(),\n upstreamClientId: this.config.clientId,\n upstreamClientSecret: this.config.clientSecret,\n upstreamTokenEndpoint: this.getTokenEndpoint(),\n });\n }\n\n protected getAuthorizationEndpoint(): string {\n return `https://login.microsoftonline.com/${this.tenantId}/oauth2/v2.0/authorize`;\n }\n\n protected getDefaultScopes(): string[] {\n return [\"openid\", \"profile\", \"email\"];\n }\n\n protected getTokenEndpoint(): string {\n return `https://login.microsoftonline.com/${this.tenantId}/oauth2/v2.0/token`;\n }\n}\n","/**\n * GitHub OAuth Provider\n * Pre-configured OAuth provider for GitHub OAuth Apps\n */\n\nimport { OAuthProxy } from \"../OAuthProxy.js\";\nimport {\n AuthProvider,\n type AuthProviderConfig,\n type OAuthSession,\n} from \"./AuthProvider.js\";\n\n/**\n * GitHub-specific session with additional user info\n */\nexport interface GitHubSession extends OAuthSession {\n username?: string;\n}\n\n/**\n * GitHub OAuth 2.0 Provider\n * Callback URL: {baseUrl}/oauth/callback\n */\nexport class GitHubProvider extends AuthProvider<GitHubSession> {\n constructor(config: AuthProviderConfig) {\n super(config);\n }\n\n protected createProxy(): OAuthProxy {\n return new OAuthProxy({\n allowedRedirectUriPatterns: this.config.allowedRedirectUriPatterns ?? [\n \"http://localhost:*\",\n \"https://*\",\n ],\n baseUrl: this.config.baseUrl,\n consentRequired: this.config.consentRequired ?? true,\n encryptionKey: this.config.encryptionKey,\n jwtSigningKey: this.config.jwtSigningKey,\n scopes: this.config.scopes ?? this.getDefaultScopes(),\n tokenStorage: this.config.tokenStorage,\n upstreamAuthorizationEndpoint: this.getAuthorizationEndpoint(),\n upstreamClientId: this.config.clientId,\n upstreamClientSecret: this.config.clientSecret,\n upstreamTokenEndpoint: this.getTokenEndpoint(),\n });\n }\n\n protected getAuthorizationEndpoint(): string {\n return \"https://github.com/login/oauth/authorize\";\n }\n\n protected getDefaultScopes(): string[] {\n return [\"read:user\", \"user:email\"];\n }\n\n protected getTokenEndpoint(): string {\n return \"https://github.com/login/oauth/access_token\";\n }\n}\n","/**\n * Google OAuth Provider\n * Pre-configured OAuth provider for Google Identity Platform\n */\n\nimport { OAuthProxy } from \"../OAuthProxy.js\";\nimport {\n AuthProvider,\n type AuthProviderConfig,\n type OAuthSession,\n} from \"./AuthProvider.js\";\n\n/**\n * Google-specific session with additional user info\n */\nexport interface GoogleSession extends OAuthSession {\n email?: string;\n}\n\n/**\n * Google OAuth 2.0 Provider\n * Callback URL: {baseUrl}/oauth/callback\n */\nexport class GoogleProvider extends AuthProvider<GoogleSession> {\n constructor(config: AuthProviderConfig) {\n super(config);\n }\n\n protected createProxy(): OAuthProxy {\n return new OAuthProxy({\n allowedRedirectUriPatterns: this.config.allowedRedirectUriPatterns ?? [\n \"http://localhost:*\",\n \"https://*\",\n ],\n baseUrl: this.config.baseUrl,\n consentRequired: this.config.consentRequired ?? true,\n encryptionKey: this.config.encryptionKey,\n jwtSigningKey: this.config.jwtSigningKey,\n scopes: this.config.scopes ?? this.getDefaultScopes(),\n tokenStorage: this.config.tokenStorage,\n upstreamAuthorizationEndpoint: this.getAuthorizationEndpoint(),\n upstreamClientId: this.config.clientId,\n upstreamClientSecret: this.config.clientSecret,\n upstreamTokenEndpoint: this.getTokenEndpoint(),\n });\n }\n\n protected getAuthorizationEndpoint(): string {\n return \"https://accounts.google.com/o/oauth2/v2/auth\";\n }\n\n protected getDefaultScopes(): string[] {\n return [\"openid\", \"profile\", \"email\"];\n }\n\n protected getTokenEndpoint(): string {\n return \"https://oauth2.googleapis.com/token\";\n }\n}\n","/**\n * Generic OAuth Provider\n * For any OAuth 2.0 compliant authorization server\n */\n\nimport { OAuthProxy } from \"../OAuthProxy.js\";\nimport {\n AuthProvider,\n type GenericOAuthProviderConfig,\n type OAuthSession,\n} from \"./AuthProvider.js\";\n\n/**\n * Generic OAuth provider for any OAuth 2.0 compliant authorization server.\n * Use when there's no built-in provider for your identity provider.\n */\nexport class OAuthProvider<\n TSession extends OAuthSession = OAuthSession,\n> extends AuthProvider<TSession> {\n protected genericConfig: GenericOAuthProviderConfig;\n\n constructor(config: GenericOAuthProviderConfig) {\n super(config);\n this.genericConfig = config;\n }\n\n protected createProxy(): OAuthProxy {\n return new OAuthProxy({\n allowedRedirectUriPatterns: this.config.allowedRedirectUriPatterns ?? [\n \"http://localhost:*\",\n \"https://*\",\n ],\n baseUrl: this.config.baseUrl,\n consentRequired: this.config.consentRequired ?? true,\n encryptionKey: this.config.encryptionKey,\n jwtSigningKey: this.config.jwtSigningKey,\n scopes: this.config.scopes ?? this.getDefaultScopes(),\n tokenStorage: this.config.tokenStorage,\n upstreamAuthorizationEndpoint: this.getAuthorizationEndpoint(),\n upstreamClientId: this.config.clientId,\n upstreamClientSecret: this.config.clientSecret,\n upstreamTokenEndpoint: this.getTokenEndpoint(),\n upstreamTokenEndpointAuthMethod:\n this.genericConfig.tokenEndpointAuthMethod ?? \"client_secret_basic\",\n });\n }\n\n protected getAuthorizationEndpoint(): string {\n return this.genericConfig.authorizationEndpoint;\n }\n\n protected getDefaultScopes(): string[] {\n return [\"openid\"];\n }\n\n protected getTokenEndpoint(): string {\n return this.genericConfig.tokenEndpoint;\n }\n}\n","/**\n * Disk-based Token Storage Implementation\n * Provides persistent file-based storage for OAuth tokens and transaction state\n */\n\nimport { mkdir, readdir, readFile, rm, stat, writeFile } from \"fs/promises\";\nimport { join } from \"path\";\n\nimport type { TokenStorage } from \"../types.js\";\n\nexport interface DiskStoreOptions {\n /**\n * How often to run cleanup (in milliseconds)\n * @default 60000 (1 minute)\n */\n cleanupIntervalMs?: number;\n\n /**\n * Directory path for storing data\n */\n directory: string;\n\n /**\n * File extension for stored files\n * @default \".json\"\n */\n fileExtension?: string;\n}\n\ninterface StorageEntry {\n expiresAt: number;\n value: unknown;\n}\n\n/**\n * Disk-based token storage with TTL support\n * Persists tokens to filesystem for survival across server restarts\n */\nexport class DiskStore implements TokenStorage {\n private cleanupInterval: NodeJS.Timeout | null = null;\n private directory: string;\n private fileExtension: string;\n\n constructor(options: DiskStoreOptions) {\n this.directory = options.directory;\n this.fileExtension = options.fileExtension || \".json\";\n\n // Ensure directory exists\n void this.ensureDirectory();\n\n // Start periodic cleanup\n const cleanupIntervalMs = options.cleanupIntervalMs || 60000;\n this.cleanupInterval = setInterval(() => {\n void this.cleanup();\n }, cleanupIntervalMs);\n }\n\n /**\n * Clean up expired entries\n */\n async cleanup(): Promise<void> {\n try {\n await this.ensureDirectory();\n const files = await readdir(this.directory);\n const now = Date.now();\n\n for (const file of files) {\n if (!file.endsWith(this.fileExtension)) {\n continue;\n }\n\n try {\n const filePath = join(this.directory, file);\n const content = await readFile(filePath, \"utf-8\");\n const entry: StorageEntry = JSON.parse(content);\n\n if (entry.expiresAt < now) {\n await rm(filePath);\n }\n } catch (error) {\n // If file is corrupted or can't be read, delete it\n console.warn(`Failed to read/parse file ${file}, deleting:`, error);\n try {\n await rm(join(this.directory, file));\n } catch {\n // Ignore deletion errors\n }\n }\n }\n } catch (error) {\n console.error(\"Cleanup failed:\", error);\n }\n }\n\n /**\n * Delete a value\n */\n async delete(key: string): Promise<void> {\n const filePath = this.getFilePath(key);\n try {\n await rm(filePath);\n } catch (error) {\n // File might not exist, which is fine\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n console.error(`Failed to delete key ${key}:`, error);\n }\n }\n }\n\n /**\n * Destroy the storage and clear cleanup interval\n */\n destroy(): void {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = null;\n }\n }\n\n /**\n * Retrieve a value\n */\n async get(key: string): Promise<null | unknown> {\n const filePath = this.getFilePath(key);\n\n try {\n const content = await readFile(filePath, \"utf-8\");\n const entry: StorageEntry = JSON.parse(content);\n\n // Check if expired\n if (entry.expiresAt < Date.now()) {\n await rm(filePath);\n return null;\n }\n\n return entry.value;\n } catch (error) {\n // File doesn't exist or is corrupted\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return null;\n }\n console.error(`Failed to read key ${key}:`, error);\n return null;\n }\n }\n\n /**\n * Save a value with optional TTL\n */\n async save(key: string, value: unknown, ttl?: number): Promise<void> {\n await this.ensureDirectory();\n\n const filePath = this.getFilePath(key);\n const expiresAt = ttl ? Date.now() + ttl * 1000 : Number.MAX_SAFE_INTEGER;\n\n const entry: StorageEntry = {\n expiresAt,\n value,\n };\n\n try {\n await writeFile(filePath, JSON.stringify(entry, null, 2), \"utf-8\");\n } catch (error) {\n console.error(`Failed to save key ${key}:`, error);\n throw error;\n }\n }\n\n /**\n * Get the number of stored items\n */\n async size(): Promise<number> {\n try {\n await this.ensureDirectory();\n const files = await readdir(this.directory);\n return files.filter((f) => f.endsWith(this.fileExtension)).length;\n } catch {\n return 0;\n }\n }\n\n /**\n * Ensure storage directory exists\n */\n private async ensureDirectory(): Promise<void> {\n try {\n const stats = await stat(this.directory);\n if (!stats.isDirectory()) {\n throw new Error(`Path ${this.directory} exists but is not a directory`);\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n await mkdir(this.directory, { recursive: true });\n } else {\n throw error;\n }\n }\n }\n\n /**\n * Get file path for a key\n */\n private getFilePath(key: string): string {\n // Sanitize key to prevent directory traversal\n const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n return join(this.directory, `${sanitizedKey}${this.fileExtension}`);\n }\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * JWKS (JSON Web Key Set) Verifier\n * Provides JWT verification using public keys from JWKS endpoints\n *\n * Requires the 'jose' package as an optional peer dependency.\n * Install with: npm install jose\n */\n\nimport type { TokenVerificationResult, TokenVerifier } from \"../types.js\";\nimport type { JWTClaims } from \"./jwtIssuer.js\";\n\n/**\n * Token verification result\n */\nexport interface JWKSVerificationResult {\n claims?: JWTClaims;\n error?: string;\n valid: boolean;\n}\n\n/**\n * JWKS configuration options\n */\nexport interface JWKSVerifierConfig {\n /**\n * Expected token audience\n */\n audience?: string;\n\n /**\n * Cache duration for JWKS keys in milliseconds\n * @default 3600000 (1 hour)\n */\n cacheDuration?: number;\n\n /**\n * Cooldown duration between JWKS refetches in milliseconds\n * @default 30000 (30 seconds)\n */\n cooldownDuration?: number;\n\n /**\n * Expected token issuer\n */\n issuer?: string;\n\n /**\n * JWKS endpoint URL (e.g., https://provider.com/.well-known/jwks.json)\n */\n jwksUri: string;\n}\n\n/**\n * JWKS Verifier\n * Verifies JWTs using public keys from a JWKS endpoint\n *\n * This class requires the 'jose' package to be installed:\n * ```bash\n * npm install jose\n * ```\n *\n * @example\n * ```typescript\n * const verifier = new JWKSVerifier({\n * jwksUri: 'https://accounts.google.com/.well-known/jwks.json',\n * audience: 'your-client-id',\n * issuer: 'https://accounts.google.com'\n * });\n *\n * const result = await verifier.verify(token);\n * if (result.valid) {\n * console.log('Token claims:', result.claims);\n * }\n * ```\n */\nexport class JWKSVerifier implements TokenVerifier {\n private config: Required<JWKSVerifierConfig>;\n private jose: any;\n private joseLoaded = false;\n private jwksCache: any;\n\n constructor(config: JWKSVerifierConfig) {\n this.config = {\n cacheDuration: 3600000, // 1 hour\n cooldownDuration: 30000, // 30 seconds\n ...config,\n audience: config.audience || \"\",\n issuer: config.issuer || \"\",\n };\n }\n\n /**\n * Get the JWKS URI being used\n */\n getJwksUri(): string {\n return this.config.jwksUri;\n }\n\n /**\n * Refresh the JWKS cache\n * Useful if you need to force a key refresh\n */\n async refreshKeys(): Promise<void> {\n await this.loadJose();\n\n // Recreate the JWKS cache to force a refresh\n this.jwksCache = this.jose.createRemoteJWKSet(\n new URL(this.config.jwksUri),\n {\n cacheMaxAge: this.config.cacheDuration,\n cooldownDuration: this.config.cooldownDuration,\n },\n );\n }\n\n /**\n * Verify a JWT token using JWKS\n *\n * @param token - The JWT token to verify\n * @returns Verification result with claims if valid\n *\n * @example\n * ```typescript\n * const result = await verifier.verify(token);\n * if (result.valid) {\n * console.log('User:', result.claims?.client_id);\n * } else {\n * console.error('Invalid token:', result.error);\n * }\n * ```\n */\n async verify(token: string): Promise<TokenVerificationResult> {\n try {\n // Ensure jose is loaded\n await this.loadJose();\n\n // Verify the token using JWKS\n const verifyOptions: any = {};\n\n if (this.config.audience) {\n verifyOptions.audience = this.config.audience;\n }\n\n if (this.config.issuer) {\n verifyOptions.issuer = this.config.issuer;\n }\n\n const { payload } = await this.jose.jwtVerify(\n token,\n this.jwksCache,\n verifyOptions,\n );\n\n // Map jose claims to TokenVerificationResult format\n // Store all claims as Record<string, unknown> for compatibility\n const claims: Record<string, unknown> = {\n aud: payload.aud,\n client_id: payload.client_id || payload.sub,\n exp: payload.exp,\n iat: payload.iat,\n iss: payload.iss,\n jti: payload.jti || \"\",\n scope: this.parseScope(payload.scope),\n ...payload, // Include all other claims\n };\n\n return {\n claims,\n valid: true,\n };\n } catch (error: any) {\n return {\n error: error.message || \"Token verification failed\",\n valid: false,\n };\n }\n }\n\n /**\n * Lazy load the jose library\n * Only loads when verification is first attempted\n */\n private async loadJose(): Promise<void> {\n if (this.joseLoaded) {\n return;\n }\n\n try {\n this.jose = await import(\"jose\");\n this.joseLoaded = true;\n\n // Create the JWKS cache with the configured URI\n this.jwksCache = this.jose.createRemoteJWKSet(\n new URL(this.config.jwksUri),\n {\n cacheMaxAge: this.config.cacheDuration,\n cooldownDuration: this.config.cooldownDuration,\n },\n );\n } catch (error: any) {\n throw new Error(\n `JWKS verification requires the 'jose' package.\\n` +\n `Install it with: npm install jose\\n\\n` +\n `If you don't need JWKS support, use HS256 signing instead (default).\\n\\n` +\n `Original error: ${error.message}`,\n );\n }\n }\n\n /**\n * Parse scope from token payload\n * Handles both string (space-separated) and array formats\n */\n private parseScope(scope: unknown): string[] {\n if (!scope) {\n return [];\n }\n\n if (typeof scope === \"string\") {\n return scope.split(\" \").filter(Boolean);\n }\n\n if (Array.isArray(scope)) {\n return scope;\n }\n\n return [];\n }\n}\n"]}