{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/plugins/mcp/client/index.ts"],"sourcesContent":["export interface McpAuthClientOptions {\n\tauthURL: string;\n\tresource?: string;\n\tallowedOrigin?: string;\n\tfetch?: typeof globalThis.fetch;\n}\n\nexport interface McpSession {\n\taccessToken: string;\n\trefreshToken: string;\n\taccessTokenExpiresAt: string;\n\trefreshTokenExpiresAt: string;\n\tclientId: string;\n\tuserId: string;\n\tscopes: string;\n}\n\ninterface OAuthDiscoveryMetadata {\n\tissuer: string;\n\tauthorization_endpoint: string;\n\ttoken_endpoint: string;\n\tregistration_endpoint?: string;\n\tjwks_uri?: string;\n\tscopes_supported?: string[];\n\tresponse_types_supported?: string[];\n\tgrant_types_supported?: string[];\n\ttoken_endpoint_auth_methods_supported?: string[];\n\tcode_challenge_methods_supported?: string[];\n\t[key: string]: unknown;\n}\n\ninterface NodeLikeRequest {\n\theaders: Record<string, string | string[] | undefined> & {\n\t\tget?: (name: string) => string | undefined;\n\t\tauthorization?: string;\n\t};\n\tget?: (name: string) => string | undefined;\n\tmcpSession?: McpSession;\n}\n\ninterface NodeLikeResponse {\n\tset?: (name: string, value: string) => void;\n\tsetHeader?: (name: string, value: string) => void;\n\tstatus?: (code: number) => { json: (body: unknown) => void };\n\twriteHead?: (code: number, headers: Record<string, string>) => void;\n\tend?: (body: string) => void;\n}\n\nexport interface McpAuthClient {\n\tverifyToken: (token: string) => Promise<McpSession | null>;\n\thandler: (\n\t\tfn: (req: Request, session: McpSession) => Response | Promise<Response>,\n\t) => (req: Request) => Promise<Response>;\n\tdiscoveryHandler: () => (req: Request) => Promise<Response>;\n\tprotectedResourceHandler: (\n\t\tserverURL: string,\n\t) => (req: Request) => Promise<Response>;\n\tmiddleware: () => (\n\t\treq: NodeLikeRequest,\n\t\tres: NodeLikeResponse,\n\t\tnext: () => void,\n\t) => Promise<void>;\n\tauthURL: string;\n}\n\nfunction buildCorsHeaders(\n\tauthURL: string,\n\tallowedOrigin?: string,\n): Record<string, string> {\n\tlet origin: string;\n\tif (allowedOrigin) {\n\t\torigin = allowedOrigin;\n\t} else {\n\t\ttry {\n\t\t\torigin = new URL(authURL).origin;\n\t\t} catch {\n\t\t\torigin = authURL;\n\t\t}\n\t}\n\treturn {\n\t\t\"Access-Control-Allow-Origin\": origin,\n\t\t\"Access-Control-Allow-Methods\": \"GET, POST, OPTIONS\",\n\t\t\"Access-Control-Allow-Headers\": \"Content-Type, Authorization\",\n\t\t\"Access-Control-Max-Age\": \"86400\",\n\t};\n}\n\nfunction makeWWWAuthenticate(authURL: string, resource?: string): string {\n\tconst resourceMetadataURL = resource\n\t\t? `${resource}/.well-known/oauth-protected-resource`\n\t\t: `${authURL}/.well-known/oauth-protected-resource`;\n\treturn `Bearer resource_metadata=\"${resourceMetadataURL}\"`;\n}\n\nfunction make401Response(authURL: string, resource?: string): Response {\n\tconst wwwAuth = makeWWWAuthenticate(authURL, resource);\n\treturn Response.json(\n\t\t{\n\t\t\tjsonrpc: \"2.0\",\n\t\t\terror: {\n\t\t\t\tcode: -32000,\n\t\t\t\tmessage: \"Unauthorized: Authentication required\",\n\t\t\t\t\"www-authenticate\": wwwAuth,\n\t\t\t},\n\t\t\tid: null,\n\t\t},\n\t\t{\n\t\t\tstatus: 401,\n\t\t\theaders: {\n\t\t\t\t\"WWW-Authenticate\": wwwAuth,\n\t\t\t},\n\t\t},\n\t);\n}\n\nfunction send401Node(\n\tres: NodeLikeResponse,\n\twwwAuth: string,\n\tmessage: string,\n): void {\n\tconst body = JSON.stringify({\n\t\tjsonrpc: \"2.0\",\n\t\terror: { code: -32000, message },\n\t\tid: null,\n\t});\n\n\tif (typeof res.set === \"function\") {\n\t\tres.set(\"WWW-Authenticate\", wwwAuth);\n\t\tres.status?.(401).json(JSON.parse(body));\n\t} else if (typeof res.writeHead === \"function\") {\n\t\tres.writeHead(401, {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\"WWW-Authenticate\": wwwAuth,\n\t\t});\n\t\tres.end?.(body);\n\t}\n}\n\nexport function createMcpAuthClient(\n\toptions: McpAuthClientOptions,\n): McpAuthClient {\n\tconst authURL = options.authURL.endsWith(\"/\")\n\t\t? options.authURL.slice(0, -1)\n\t\t: options.authURL;\n\tconst fetchFn = options.fetch ?? globalThis.fetch;\n\tconst corsHeaders = buildCorsHeaders(authURL, options.allowedOrigin);\n\n\tconst verifyToken = async (token: string): Promise<McpSession | null> => {\n\t\ttry {\n\t\t\tconst response = await fetchFn(`${authURL}/mcp/get-session`, {\n\t\t\t\tmethod: \"GET\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst data = await response.json();\n\t\t\tif (!data || !data.userId) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn data as McpSession;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t};\n\n\tconst handler: McpAuthClient[\"handler\"] = (fn) => {\n\t\treturn async (req: Request) => {\n\t\t\tif (req.method === \"OPTIONS\") {\n\t\t\t\treturn new Response(null, { status: 204, headers: corsHeaders });\n\t\t\t}\n\n\t\t\tconst authHeader = req.headers.get(\"Authorization\");\n\t\t\tif (!authHeader || !authHeader.startsWith(\"Bearer \")) {\n\t\t\t\treturn make401Response(authURL, options.resource);\n\t\t\t}\n\n\t\t\tconst token = authHeader.slice(7);\n\t\t\tconst session = await verifyToken(token);\n\t\t\tif (!session) {\n\t\t\t\treturn make401Response(authURL, options.resource);\n\t\t\t}\n\n\t\t\treturn fn(req, session);\n\t\t};\n\t};\n\n\tconst discoveryHandler: McpAuthClient[\"discoveryHandler\"] = () => {\n\t\tlet cachedMetadata: OAuthDiscoveryMetadata | null = null;\n\t\tlet cacheTime = 0;\n\t\tconst CACHE_TTL = 60_000;\n\n\t\treturn async (_req: Request) => {\n\t\t\tconst now = Date.now();\n\t\t\tif (cachedMetadata && now - cacheTime < CACHE_TTL) {\n\t\t\t\treturn Response.json(cachedMetadata, { headers: corsHeaders });\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst response = await fetchFn(\n\t\t\t\t\t`${authURL}/.well-known/oauth-authorization-server`,\n\t\t\t\t);\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\treturn Response.json(\n\t\t\t\t\t\t{ error: \"Failed to fetch discovery metadata\" },\n\t\t\t\t\t\t{ status: 502, headers: corsHeaders },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcachedMetadata = (await response.json()) as OAuthDiscoveryMetadata;\n\t\t\t\tcacheTime = now;\n\t\t\t\treturn Response.json(cachedMetadata, { headers: corsHeaders });\n\t\t\t} catch {\n\t\t\t\treturn Response.json(\n\t\t\t\t\t{ error: \"Better Auth server unreachable\" },\n\t\t\t\t\t{ status: 502, headers: corsHeaders },\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\t};\n\n\tconst protectedResourceHandler: McpAuthClient[\"protectedResourceHandler\"] = (\n\t\tserverURL: string,\n\t) => {\n\t\tconst resource = options.resource ?? new URL(serverURL).origin;\n\t\tconst metadata = {\n\t\t\tresource,\n\t\t\tauthorization_servers: [authURL],\n\t\t\tbearer_methods_supported: [\"header\"],\n\t\t\tscopes_supported: [\"openid\", \"profile\", \"email\", \"offline_access\"],\n\t\t};\n\n\t\treturn async (_req: Request) => {\n\t\t\treturn Response.json(metadata, { headers: corsHeaders });\n\t\t};\n\t};\n\n\tconst middleware: McpAuthClient[\"middleware\"] = () => {\n\t\treturn async (\n\t\t\treq: NodeLikeRequest,\n\t\t\tres: NodeLikeResponse,\n\t\t\tnext: () => void,\n\t\t) => {\n\t\t\tconst authHeader =\n\t\t\t\treq.headers?.authorization ??\n\t\t\t\treq.headers?.get?.(\"Authorization\") ??\n\t\t\t\treq.get?.(\"Authorization\");\n\n\t\t\tif (!authHeader || !authHeader.startsWith(\"Bearer \")) {\n\t\t\t\tsend401Node(\n\t\t\t\t\tres,\n\t\t\t\t\tmakeWWWAuthenticate(authURL, options.resource),\n\t\t\t\t\t\"Unauthorized: Authentication required\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst token = authHeader.slice(7);\n\t\t\tconst session = await verifyToken(token);\n\t\t\tif (!session) {\n\t\t\t\tsend401Node(\n\t\t\t\t\tres,\n\t\t\t\t\tmakeWWWAuthenticate(authURL, options.resource),\n\t\t\t\t\t\"Invalid or expired token\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treq.mcpSession = session;\n\t\t\tnext();\n\t\t};\n\t};\n\n\treturn {\n\t\tverifyToken,\n\t\thandler,\n\t\tdiscoveryHandler,\n\t\tprotectedResourceHandler,\n\t\tmiddleware,\n\t\tauthURL,\n\t};\n}\n"],"mappings":"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"}