better-auth
Version:
The most comprehensive authentication framework for TypeScript.
1 lines • 50.8 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","names":["accessToken","accessTokenExpiresAt","refreshTokenExpiresAt"],"sources":["../../../src/plugins/mcp/index.ts"],"sourcesContent":["import type {\n\tBetterAuthOptions,\n\tBetterAuthPlugin,\n\tGenericEndpointContext,\n} from \"@better-auth/core\";\nimport {\n\tcreateAuthEndpoint,\n\tcreateAuthMiddleware,\n} from \"@better-auth/core/api\";\nimport { isProduction, logger } from \"@better-auth/core/env\";\nimport { getWebcryptoSubtle } from \"@better-auth/utils\";\nimport { base64 } from \"@better-auth/utils/base64\";\nimport { createHash } from \"@better-auth/utils/hash\";\nimport { SignJWT } from \"jose\";\nimport * as z from \"zod\";\nimport { APIError, getSessionFromCtx } from \"../../api\";\nimport { parseSetCookieHeader } from \"../../cookies\";\nimport { generateRandomString } from \"../../crypto\";\nimport { HIDE_METADATA } from \"../../utils\";\nimport { getBaseURL } from \"../../utils/url\";\nimport type {\n\tClient,\n\tCodeVerificationValue,\n\tOAuthAccessToken,\n\tOIDCMetadata,\n\tOIDCOptions,\n} from \"../oidc-provider\";\nimport { oidcProvider } from \"../oidc-provider\";\nimport { schema } from \"../oidc-provider/schema\";\nimport { parsePrompt } from \"../oidc-provider/utils/prompt\";\nimport { authorizeMCPOAuth } from \"./authorize\";\n\ninterface MCPOptions {\n\tloginPage: string;\n\tresource?: string | undefined;\n\toidcConfig?: OIDCOptions | undefined;\n}\n\nexport const getMCPProviderMetadata = (\n\tctx: GenericEndpointContext,\n\toptions?: OIDCOptions | undefined,\n): OIDCMetadata => {\n\tconst issuer = ctx.context.options.baseURL as string;\n\tconst baseURL = ctx.context.baseURL;\n\tif (!issuer || !baseURL) {\n\t\tthrow new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\terror: \"invalid_issuer\",\n\t\t\terror_description:\n\t\t\t\t\"issuer or baseURL is not set. If you're the app developer, please make sure to set the `baseURL` in your auth config.\",\n\t\t});\n\t}\n\treturn {\n\t\tissuer,\n\t\tauthorization_endpoint: `${baseURL}/mcp/authorize`,\n\t\ttoken_endpoint: `${baseURL}/mcp/token`,\n\t\tuserinfo_endpoint: `${baseURL}/mcp/userinfo`,\n\t\tjwks_uri: `${baseURL}/mcp/jwks`,\n\t\tregistration_endpoint: `${baseURL}/mcp/register`,\n\t\tscopes_supported: [\"openid\", \"profile\", \"email\", \"offline_access\"],\n\t\tresponse_types_supported: [\"code\"],\n\t\tresponse_modes_supported: [\"query\"],\n\t\tgrant_types_supported: [\"authorization_code\", \"refresh_token\"],\n\t\tacr_values_supported: [\n\t\t\t\"urn:mace:incommon:iap:silver\",\n\t\t\t\"urn:mace:incommon:iap:bronze\",\n\t\t],\n\t\tsubject_types_supported: [\"public\"],\n\t\tid_token_signing_alg_values_supported: [\"RS256\", \"none\"],\n\t\ttoken_endpoint_auth_methods_supported: [\n\t\t\t\"client_secret_basic\",\n\t\t\t\"client_secret_post\",\n\t\t\t\"none\",\n\t\t],\n\t\tcode_challenge_methods_supported: [\"S256\"],\n\t\tclaims_supported: [\n\t\t\t\"sub\",\n\t\t\t\"iss\",\n\t\t\t\"aud\",\n\t\t\t\"exp\",\n\t\t\t\"nbf\",\n\t\t\t\"iat\",\n\t\t\t\"jti\",\n\t\t\t\"email\",\n\t\t\t\"email_verified\",\n\t\t\t\"name\",\n\t\t],\n\t\t...options?.metadata,\n\t};\n};\n\nexport const getMCPProtectedResourceMetadata = (\n\tctx: GenericEndpointContext,\n\toptions?: MCPOptions | undefined,\n) => {\n\tconst baseURL = ctx.context.baseURL;\n\tconst origin = new URL(baseURL).origin;\n\n\treturn {\n\t\tresource: options?.resource ?? origin,\n\t\tauthorization_servers: [origin],\n\t\tjwks_uri: options?.oidcConfig?.metadata?.jwks_uri ?? `${baseURL}/mcp/jwks`,\n\t\tscopes_supported: options?.oidcConfig?.metadata?.scopes_supported ?? [\n\t\t\t\"openid\",\n\t\t\t\"profile\",\n\t\t\t\"email\",\n\t\t\t\"offline_access\",\n\t\t],\n\t\tbearer_methods_supported: [\"header\"],\n\t\tresource_signing_alg_values_supported: [\"RS256\", \"none\"],\n\t};\n};\n\nconst registerMcpClientBodySchema = z.object({\n\tredirect_uris: z.array(z.string()),\n\ttoken_endpoint_auth_method: z\n\t\t.enum([\"none\", \"client_secret_basic\", \"client_secret_post\"])\n\t\t.default(\"client_secret_basic\")\n\t\t.optional(),\n\tgrant_types: z\n\t\t.array(\n\t\t\tz.enum([\n\t\t\t\t\"authorization_code\",\n\t\t\t\t\"implicit\",\n\t\t\t\t\"password\",\n\t\t\t\t\"client_credentials\",\n\t\t\t\t\"refresh_token\",\n\t\t\t\t\"urn:ietf:params:oauth:grant-type:jwt-bearer\",\n\t\t\t\t\"urn:ietf:params:oauth:grant-type:saml2-bearer\",\n\t\t\t]),\n\t\t)\n\t\t.default([\"authorization_code\"])\n\t\t.optional(),\n\tresponse_types: z\n\t\t.array(z.enum([\"code\", \"token\"]))\n\t\t.default([\"code\"])\n\t\t.optional(),\n\tclient_name: z.string().optional(),\n\tclient_uri: z.string().optional(),\n\tlogo_uri: z.string().optional(),\n\tscope: z.string().optional(),\n\tcontacts: z.array(z.string()).optional(),\n\ttos_uri: z.string().optional(),\n\tpolicy_uri: z.string().optional(),\n\tjwks_uri: z.string().optional(),\n\tjwks: z.record(z.string(), z.any()).optional(),\n\tmetadata: z.record(z.any(), z.any()).optional(),\n\tsoftware_id: z.string().optional(),\n\tsoftware_version: z.string().optional(),\n\tsoftware_statement: z.string().optional(),\n});\n\nconst mcpOAuthTokenBodySchema = z.record(z.any(), z.any());\n\nexport const mcp = (options: MCPOptions) => {\n\tconst opts = {\n\t\tcodeExpiresIn: 600,\n\t\tdefaultScope: \"openid\",\n\t\taccessTokenExpiresIn: 3600,\n\t\trefreshTokenExpiresIn: 604800,\n\t\tallowPlainCodeChallengeMethod: true,\n\t\t...options.oidcConfig,\n\t\tloginPage: options.loginPage,\n\t\tscopes: [\n\t\t\t\"openid\",\n\t\t\t\"profile\",\n\t\t\t\"email\",\n\t\t\t\"offline_access\",\n\t\t\t...(options.oidcConfig?.scopes || []),\n\t\t],\n\t};\n\tconst modelName = {\n\t\toauthClient: \"oauthApplication\",\n\t\toauthAccessToken: \"oauthAccessToken\",\n\t\toauthConsent: \"oauthConsent\",\n\t};\n\tconst provider = oidcProvider(opts);\n\treturn {\n\t\tid: \"mcp\",\n\t\thooks: {\n\t\t\tafter: [\n\t\t\t\t{\n\t\t\t\t\tmatcher() {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t},\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tconst cookie = await ctx.getSignedCookie(\n\t\t\t\t\t\t\t\"oidc_login_prompt\",\n\t\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst cookieName = ctx.context.authCookies.sessionToken.name;\n\t\t\t\t\t\tconst parsedSetCookieHeader = parseSetCookieHeader(\n\t\t\t\t\t\t\tctx.context.responseHeaders?.get(\"set-cookie\") || \"\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst hasSessionToken = parsedSetCookieHeader.has(cookieName);\n\t\t\t\t\t\tif (!cookie || !hasSessionToken) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tctx.setCookie(\"oidc_login_prompt\", \"\", {\n\t\t\t\t\t\t\tmaxAge: 0,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst sessionCookie = parsedSetCookieHeader.get(cookieName)?.value;\n\t\t\t\t\t\tconst sessionToken = sessionCookie?.split(\".\")[0]!;\n\t\t\t\t\t\tif (!sessionToken) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst session =\n\t\t\t\t\t\t\t(await ctx.context.internalAdapter.findSession(sessionToken)) ||\n\t\t\t\t\t\t\tctx.context.newSession;\n\t\t\t\t\t\tif (!session) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Remove \"login\" from prompt since user just logged in\n\t\t\t\t\t\tconst promptSet = parsePrompt(String(ctx.query?.prompt));\n\t\t\t\t\t\tif (promptSet.has(\"login\")) {\n\t\t\t\t\t\t\tconst newPromptSet = new Set(promptSet);\n\t\t\t\t\t\t\tnewPromptSet.delete(\"login\");\n\t\t\t\t\t\t\tctx.query = {\n\t\t\t\t\t\t\t\t...ctx.query,\n\t\t\t\t\t\t\t\tprompt: Array.from(newPromptSet).join(\" \"),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tctx.context.session = session;\n\t\t\t\t\t\tconst response = await authorizeMCPOAuth(ctx, opts);\n\t\t\t\t\t\treturn response;\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\tendpoints: {\n\t\t\toAuthConsent: provider.endpoints.oAuthConsent,\n\t\t\tgetMcpOAuthConfig: createAuthEndpoint(\n\t\t\t\t\"/.well-known/oauth-authorization-server\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\tmetadata: HIDE_METADATA,\n\t\t\t\t},\n\t\t\t\tasync (c) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst metadata = getMCPProviderMetadata(c, options);\n\t\t\t\t\t\treturn c.json(metadata);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tconsole.log(e);\n\t\t\t\t\t\treturn c.json(null);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t),\n\t\t\tgetMCPProtectedResource: createAuthEndpoint(\n\t\t\t\t\"/.well-known/oauth-protected-resource\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\tmetadata: HIDE_METADATA,\n\t\t\t\t},\n\t\t\t\tasync (c) => {\n\t\t\t\t\tconst metadata = getMCPProtectedResourceMetadata(c, options);\n\t\t\t\t\treturn c.json(metadata);\n\t\t\t\t},\n\t\t\t),\n\t\t\tmcpOAuthAuthorize: createAuthEndpoint(\n\t\t\t\t\"/mcp/authorize\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\tquery: z.record(z.string(), z.any()),\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\tdescription: \"Authorize an OAuth2 request using MCP\",\n\t\t\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\t\t\tdescription: \"Authorization response generated successfully\",\n\t\t\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: true,\n\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"Authorization response, contents depend on the authorize function implementation\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync (ctx) => {\n\t\t\t\t\treturn authorizeMCPOAuth(ctx, opts);\n\t\t\t\t},\n\t\t\t),\n\t\t\tmcpOAuthToken: createAuthEndpoint(\n\t\t\t\t\"/mcp/token\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: mcpOAuthTokenBodySchema,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t...HIDE_METADATA,\n\t\t\t\t\t\tallowedMediaTypes: [\n\t\t\t\t\t\t\t\"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t\"application/json\",\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync (ctx) => {\n\t\t\t\t\t//cors\n\t\t\t\t\tctx.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n\t\t\t\t\tctx.setHeader(\"Access-Control-Allow-Methods\", \"POST, OPTIONS\");\n\t\t\t\t\tctx.setHeader(\n\t\t\t\t\t\t\"Access-Control-Allow-Headers\",\n\t\t\t\t\t\t\"Content-Type, Authorization\",\n\t\t\t\t\t);\n\t\t\t\t\tctx.setHeader(\"Access-Control-Max-Age\", \"86400\");\n\n\t\t\t\t\tlet { body } = ctx;\n\t\t\t\t\tif (!body) {\n\t\t\t\t\t\tthrow ctx.error(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\terror_description: \"request body not found\",\n\t\t\t\t\t\t\terror: \"invalid_request\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tif (body instanceof FormData) {\n\t\t\t\t\t\tbody = Object.fromEntries(body.entries());\n\t\t\t\t\t}\n\t\t\t\t\tif (!(body instanceof Object)) {\n\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\terror_description: \"request body is not an object\",\n\t\t\t\t\t\t\terror: \"invalid_request\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tlet { client_id, client_secret } = body;\n\t\t\t\t\tconst authorization =\n\t\t\t\t\t\tctx.request?.headers.get(\"authorization\") || null;\n\t\t\t\t\tif (\n\t\t\t\t\t\tauthorization &&\n\t\t\t\t\t\t!client_id &&\n\t\t\t\t\t\t!client_secret &&\n\t\t\t\t\t\tauthorization.startsWith(\"Basic \")\n\t\t\t\t\t) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst encoded = authorization.replace(\"Basic \", \"\");\n\t\t\t\t\t\t\tconst decoded = new TextDecoder().decode(base64.decode(encoded));\n\t\t\t\t\t\t\tif (!decoded.includes(\":\")) {\n\t\t\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\t\t\terror_description: \"invalid authorization header format\",\n\t\t\t\t\t\t\t\t\terror: \"invalid_client\",\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst [id, secret] = decoded.split(\":\");\n\t\t\t\t\t\t\tif (!id || !secret) {\n\t\t\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\t\t\terror_description: \"invalid authorization header format\",\n\t\t\t\t\t\t\t\t\terror: \"invalid_client\",\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tclient_id = id;\n\t\t\t\t\t\t\tclient_secret = secret;\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\t\terror_description: \"invalid authorization header format\",\n\t\t\t\t\t\t\t\terror: \"invalid_client\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconst {\n\t\t\t\t\t\tgrant_type,\n\t\t\t\t\t\tcode,\n\t\t\t\t\t\tredirect_uri,\n\t\t\t\t\t\trefresh_token,\n\t\t\t\t\t\tcode_verifier,\n\t\t\t\t\t} = body;\n\t\t\t\t\tif (grant_type === \"refresh_token\") {\n\t\t\t\t\t\tif (!refresh_token) {\n\t\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\terror_description: \"refresh_token is required\",\n\t\t\t\t\t\t\t\terror: \"invalid_request\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst token = await ctx.context.adapter.findOne<OAuthAccessToken>({\n\t\t\t\t\t\t\tmodel: \"oauthAccessToken\",\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"refreshToken\",\n\t\t\t\t\t\t\t\t\tvalue: refresh_token.toString(),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (!token) {\n\t\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\t\terror_description: \"invalid refresh token\",\n\t\t\t\t\t\t\t\terror: \"invalid_grant\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (token.clientId !== client_id?.toString()) {\n\t\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\t\terror_description: \"invalid client_id\",\n\t\t\t\t\t\t\t\terror: \"invalid_client\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (token.refreshTokenExpiresAt < new Date()) {\n\t\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\t\terror_description: \"refresh token expired\",\n\t\t\t\t\t\t\t\terror: \"invalid_grant\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst accessToken = generateRandomString(32, \"a-z\", \"A-Z\");\n\t\t\t\t\t\tconst newRefreshToken = generateRandomString(32, \"a-z\", \"A-Z\");\n\t\t\t\t\t\tconst accessTokenExpiresAt = new Date(\n\t\t\t\t\t\t\tDate.now() + opts.accessTokenExpiresIn * 1000,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst refreshTokenExpiresAt = new Date(\n\t\t\t\t\t\t\tDate.now() + opts.refreshTokenExpiresIn * 1000,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tawait ctx.context.adapter.create({\n\t\t\t\t\t\t\tmodel: modelName.oauthAccessToken,\n\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\taccessToken,\n\t\t\t\t\t\t\t\trefreshToken: newRefreshToken,\n\t\t\t\t\t\t\t\taccessTokenExpiresAt,\n\t\t\t\t\t\t\t\trefreshTokenExpiresAt,\n\t\t\t\t\t\t\t\tclientId: client_id.toString(),\n\t\t\t\t\t\t\t\tuserId: token.userId,\n\t\t\t\t\t\t\t\tscopes: token.scopes,\n\t\t\t\t\t\t\t\tcreatedAt: new Date(),\n\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\t\taccess_token: accessToken,\n\t\t\t\t\t\t\ttoken_type: \"bearer\",\n\t\t\t\t\t\t\texpires_in: opts.accessTokenExpiresIn,\n\t\t\t\t\t\t\trefresh_token: newRefreshToken,\n\t\t\t\t\t\t\tscope: token.scopes,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!code) {\n\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\terror_description: \"code is required\",\n\t\t\t\t\t\t\terror: \"invalid_request\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tif (opts.requirePKCE && !code_verifier) {\n\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\terror_description: \"code verifier is missing\",\n\t\t\t\t\t\t\terror: \"invalid_request\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t/**\n\t\t\t\t\t * We need to check if the code is valid before we can proceed\n\t\t\t\t\t * with the rest of the request.\n\t\t\t\t\t */\n\t\t\t\t\tconst verificationValue =\n\t\t\t\t\t\tawait ctx.context.internalAdapter.findVerificationValue(\n\t\t\t\t\t\t\tcode.toString(),\n\t\t\t\t\t\t);\n\t\t\t\t\tif (!verificationValue) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\terror_description: \"invalid code\",\n\t\t\t\t\t\t\terror: \"invalid_grant\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tif (verificationValue.expiresAt < new Date()) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\terror_description: \"code expired\",\n\t\t\t\t\t\t\terror: \"invalid_grant\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationValue(\n\t\t\t\t\t\tverificationValue.id,\n\t\t\t\t\t);\n\n\t\t\t\t\tif (!client_id) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\terror_description: \"client_id is required\",\n\t\t\t\t\t\t\terror: \"invalid_client\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tif (!grant_type) {\n\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\terror_description: \"grant_type is required\",\n\t\t\t\t\t\t\terror: \"invalid_request\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tif (grant_type !== \"authorization_code\") {\n\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\terror_description: \"grant_type must be 'authorization_code'\",\n\t\t\t\t\t\t\terror: \"unsupported_grant_type\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!redirect_uri) {\n\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\terror_description: \"redirect_uri is required\",\n\t\t\t\t\t\t\terror: \"invalid_request\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst client = await ctx.context.adapter\n\t\t\t\t\t\t.findOne<Record<string, any>>({\n\t\t\t\t\t\t\tmodel: modelName.oauthClient,\n\t\t\t\t\t\t\twhere: [{ field: \"clientId\", value: client_id.toString() }],\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.then((res) => {\n\t\t\t\t\t\t\tif (!res) {\n\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t...res,\n\t\t\t\t\t\t\t\tredirectUrls: res.redirectUrls.split(\",\"),\n\t\t\t\t\t\t\t\tmetadata: res.metadata ? JSON.parse(res.metadata) : {},\n\t\t\t\t\t\t\t} as Client;\n\t\t\t\t\t\t});\n\t\t\t\t\tif (!client) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\terror_description: \"invalid client_id\",\n\t\t\t\t\t\t\terror: \"invalid_client\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tif (client.disabled) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\terror_description: \"client is disabled\",\n\t\t\t\t\t\t\terror: \"invalid_client\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\t// For public clients (type: 'public'), validate PKCE instead of client_secret\n\t\t\t\t\tif (client.type === \"public\") {\n\t\t\t\t\t\t// Public clients must use PKCE\n\t\t\t\t\t\tif (!code_verifier) {\n\t\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\terror_description:\n\t\t\t\t\t\t\t\t\t\"code verifier is required for public clients\",\n\t\t\t\t\t\t\t\terror: \"invalid_request\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// PKCE validation happens later in the flow, so we skip client_secret validation\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// For confidential clients, validate client_secret\n\t\t\t\t\t\tif (!client_secret) {\n\t\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\t\terror_description:\n\t\t\t\t\t\t\t\t\t\"client_secret is required for confidential clients\",\n\t\t\t\t\t\t\t\terror: \"invalid_client\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst isValidSecret =\n\t\t\t\t\t\t\tclient.clientSecret === client_secret.toString();\n\t\t\t\t\t\tif (!isValidSecret) {\n\t\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\t\terror_description: \"invalid client_secret\",\n\t\t\t\t\t\t\t\terror: \"invalid_client\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconst value = JSON.parse(\n\t\t\t\t\t\tverificationValue.value,\n\t\t\t\t\t) as CodeVerificationValue;\n\t\t\t\t\tif (value.clientId !== client_id.toString()) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\terror_description: \"invalid client_id\",\n\t\t\t\t\t\t\terror: \"invalid_client\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tif (value.redirectURI !== redirect_uri.toString()) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\terror_description: \"invalid redirect_uri\",\n\t\t\t\t\t\t\terror: \"invalid_client\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tif (value.codeChallenge && !code_verifier) {\n\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\terror_description: \"code verifier is missing\",\n\t\t\t\t\t\t\terror: \"invalid_request\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst challenge =\n\t\t\t\t\t\tvalue.codeChallengeMethod === \"plain\"\n\t\t\t\t\t\t\t? code_verifier\n\t\t\t\t\t\t\t: await createHash(\"SHA-256\", \"base64urlnopad\").digest(\n\t\t\t\t\t\t\t\t\tcode_verifier,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\tif (challenge !== value.codeChallenge) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\terror_description: \"code verification failed\",\n\t\t\t\t\t\t\terror: \"invalid_request\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst requestedScopes = value.scope;\n\t\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationValue(\n\t\t\t\t\t\tverificationValue.id,\n\t\t\t\t\t);\n\t\t\t\t\tconst accessToken = generateRandomString(32, \"a-z\", \"A-Z\");\n\t\t\t\t\tconst refreshToken = generateRandomString(32, \"A-Z\", \"a-z\");\n\t\t\t\t\tconst accessTokenExpiresAt = new Date(\n\t\t\t\t\t\tDate.now() + opts.accessTokenExpiresIn * 1000,\n\t\t\t\t\t);\n\t\t\t\t\tconst refreshTokenExpiresAt = new Date(\n\t\t\t\t\t\tDate.now() + opts.refreshTokenExpiresIn * 1000,\n\t\t\t\t\t);\n\t\t\t\t\tawait ctx.context.adapter.create({\n\t\t\t\t\t\tmodel: modelName.oauthAccessToken,\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\taccessToken,\n\t\t\t\t\t\t\trefreshToken,\n\t\t\t\t\t\t\taccessTokenExpiresAt,\n\t\t\t\t\t\t\trefreshTokenExpiresAt,\n\t\t\t\t\t\t\tclientId: client_id.toString(),\n\t\t\t\t\t\t\tuserId: value.userId,\n\t\t\t\t\t\t\tscopes: requestedScopes.join(\" \"),\n\t\t\t\t\t\t\tcreatedAt: new Date(),\n\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\tconst user = await ctx.context.internalAdapter.findUserById(\n\t\t\t\t\t\tvalue.userId,\n\t\t\t\t\t);\n\t\t\t\t\tif (!user) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\terror_description: \"user not found\",\n\t\t\t\t\t\t\terror: \"invalid_grant\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tlet secretKey = {\n\t\t\t\t\t\talg: \"HS256\",\n\t\t\t\t\t\tkey: await getWebcryptoSubtle().generateKey(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: \"HMAC\",\n\t\t\t\t\t\t\t\thash: \"SHA-256\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t\t[\"sign\", \"verify\"],\n\t\t\t\t\t\t),\n\t\t\t\t\t};\n\t\t\t\t\tconst profile = {\n\t\t\t\t\t\tgiven_name: user.name.split(\" \")[0]!,\n\t\t\t\t\t\tfamily_name: user.name.split(\" \")[1]!,\n\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\tprofile: user.image,\n\t\t\t\t\t\tupdated_at: Math.floor(new Date(user.updatedAt).getTime() / 1000),\n\t\t\t\t\t};\n\t\t\t\t\tconst email = {\n\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\temail_verified: user.emailVerified,\n\t\t\t\t\t};\n\t\t\t\t\tconst userClaims = {\n\t\t\t\t\t\t...(requestedScopes.includes(\"profile\") ? profile : {}),\n\t\t\t\t\t\t...(requestedScopes.includes(\"email\") ? email : {}),\n\t\t\t\t\t};\n\n\t\t\t\t\tconst additionalUserClaims = opts.getAdditionalUserInfoClaim\n\t\t\t\t\t\t? await opts.getAdditionalUserInfoClaim(\n\t\t\t\t\t\t\t\tuser,\n\t\t\t\t\t\t\t\trequestedScopes,\n\t\t\t\t\t\t\t\tclient,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: {};\n\n\t\t\t\t\tconst idToken = await new SignJWT({\n\t\t\t\t\t\tsub: user.id,\n\t\t\t\t\t\taud: client_id.toString(),\n\t\t\t\t\t\tiat: Date.now(),\n\t\t\t\t\t\tauth_time: ctx.context.session\n\t\t\t\t\t\t\t? new Date(ctx.context.session.session.createdAt).getTime()\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\tnonce: value.nonce,\n\t\t\t\t\t\tacr: \"urn:mace:incommon:iap:silver\", // default to silver - ⚠︎ this should be configurable and should be validated against the client's metadata\n\t\t\t\t\t\t...userClaims,\n\t\t\t\t\t\t...additionalUserClaims,\n\t\t\t\t\t})\n\t\t\t\t\t\t.setProtectedHeader({ alg: secretKey.alg })\n\t\t\t\t\t\t.setIssuedAt()\n\t\t\t\t\t\t.setExpirationTime(\n\t\t\t\t\t\t\tMath.floor(Date.now() / 1000) + opts.accessTokenExpiresIn,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.sign(secretKey.key);\n\t\t\t\t\treturn ctx.json(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\taccess_token: accessToken,\n\t\t\t\t\t\t\ttoken_type: \"Bearer\",\n\t\t\t\t\t\t\texpires_in: opts.accessTokenExpiresIn,\n\t\t\t\t\t\t\trefresh_token: requestedScopes.includes(\"offline_access\")\n\t\t\t\t\t\t\t\t? refreshToken\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\tscope: requestedScopes.join(\" \"),\n\t\t\t\t\t\t\tid_token: requestedScopes.includes(\"openid\")\n\t\t\t\t\t\t\t\t? idToken\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\t\"Cache-Control\": \"no-store\",\n\t\t\t\t\t\t\t\tPragma: \"no-cache\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t),\n\t\t\tregisterMcpClient: createAuthEndpoint(\n\t\t\t\t\"/mcp/register\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: registerMcpClientBodySchema,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\tdescription: \"Register an OAuth2 application\",\n\t\t\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\t\t\tdescription: \"OAuth2 application registered successfully\",\n\t\t\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tname: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Name of the OAuth2 application\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\ticon: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Icon URL for the application\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tmetadata: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Additional metadata for the application\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tclientId: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Unique identifier for the client\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tclientSecret: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Secret key for the client. Not included for public clients.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tredirectUrls: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\titems: { type: \"string\", format: \"uri\" },\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"List of allowed redirect URLs\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Type of the client\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tenum: [\"web\", \"public\"],\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tauthenticationScheme: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Authentication scheme used by the client\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tenum: [\"client_secret\", \"none\"],\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Whether the client is disabled\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tenum: [false],\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tuserId: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"ID of the user who registered the client, null if registered anonymously\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tcreatedAt: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Creation timestamp\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tupdatedAt: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Last update timestamp\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"clientId\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"redirectUrls\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"authenticationScheme\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"disabled\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"createdAt\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"updatedAt\",\n\t\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync (ctx) => {\n\t\t\t\t\tconst body = ctx.body;\n\t\t\t\t\tconst session = await getSessionFromCtx(ctx);\n\t\t\t\t\tctx.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n\t\t\t\t\tctx.setHeader(\"Access-Control-Allow-Methods\", \"POST, OPTIONS\");\n\t\t\t\t\tctx.setHeader(\n\t\t\t\t\t\t\"Access-Control-Allow-Headers\",\n\t\t\t\t\t\t\"Content-Type, Authorization\",\n\t\t\t\t\t);\n\t\t\t\t\tctx.setHeader(\"Access-Control-Max-Age\", \"86400\");\n\t\t\t\t\tctx.headers?.set(\"Access-Control-Max-Age\", \"86400\");\n\t\t\t\t\tif (\n\t\t\t\t\t\t(!body.grant_types ||\n\t\t\t\t\t\t\tbody.grant_types.includes(\"authorization_code\") ||\n\t\t\t\t\t\t\tbody.grant_types.includes(\"implicit\")) &&\n\t\t\t\t\t\t(!body.redirect_uris || body.redirect_uris.length === 0)\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\terror: \"invalid_redirect_uri\",\n\t\t\t\t\t\t\terror_description:\n\t\t\t\t\t\t\t\t\"Redirect URIs are required for authorization_code and implicit grant types\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tif (body.grant_types && body.response_types) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tbody.grant_types.includes(\"authorization_code\") &&\n\t\t\t\t\t\t\t!body.response_types.includes(\"code\")\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\terror: \"invalid_client_metadata\",\n\t\t\t\t\t\t\t\terror_description:\n\t\t\t\t\t\t\t\t\t\"When 'authorization_code' grant type is used, 'code' response type must be included\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tbody.grant_types.includes(\"implicit\") &&\n\t\t\t\t\t\t\t!body.response_types.includes(\"token\")\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\terror: \"invalid_client_metadata\",\n\t\t\t\t\t\t\t\terror_description:\n\t\t\t\t\t\t\t\t\t\"When 'implicit' grant type is used, 'token' response type must be included\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst clientId =\n\t\t\t\t\t\topts.generateClientId?.() || generateRandomString(32, \"a-z\", \"A-Z\");\n\t\t\t\t\tconst clientSecret =\n\t\t\t\t\t\topts.generateClientSecret?.() ||\n\t\t\t\t\t\tgenerateRandomString(32, \"a-z\", \"A-Z\");\n\n\t\t\t\t\t// Determine client type based on auth method\n\t\t\t\t\tconst clientType =\n\t\t\t\t\t\tbody.token_endpoint_auth_method === \"none\" ? \"public\" : \"web\";\n\t\t\t\t\tconst finalClientSecret = clientType === \"public\" ? \"\" : clientSecret;\n\n\t\t\t\t\tawait ctx.context.adapter.create({\n\t\t\t\t\t\tmodel: modelName.oauthClient,\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\tname: body.client_name,\n\t\t\t\t\t\t\ticon: body.logo_uri,\n\t\t\t\t\t\t\tmetadata: body.metadata ? JSON.stringify(body.metadata) : null,\n\t\t\t\t\t\t\tclientId: clientId,\n\t\t\t\t\t\t\tclientSecret: finalClientSecret,\n\t\t\t\t\t\t\tredirectUrls: body.redirect_uris.join(\",\"),\n\t\t\t\t\t\t\ttype: clientType,\n\t\t\t\t\t\t\tauthenticationScheme:\n\t\t\t\t\t\t\t\tbody.token_endpoint_auth_method || \"client_secret_basic\",\n\t\t\t\t\t\t\tdisabled: false,\n\t\t\t\t\t\t\tuserId: session?.session.userId,\n\t\t\t\t\t\t\tcreatedAt: new Date(),\n\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\n\t\t\t\t\tconst responseData = {\n\t\t\t\t\t\tclient_id: clientId,\n\t\t\t\t\t\tclient_id_issued_at: Math.floor(Date.now() / 1000),\n\t\t\t\t\t\tredirect_uris: body.redirect_uris,\n\t\t\t\t\t\ttoken_endpoint_auth_method:\n\t\t\t\t\t\t\tbody.token_endpoint_auth_method || \"client_secret_basic\",\n\t\t\t\t\t\tgrant_types: body.grant_types || [\"authorization_code\"],\n\t\t\t\t\t\tresponse_types: body.response_types || [\"code\"],\n\t\t\t\t\t\tclient_name: body.client_name,\n\t\t\t\t\t\tclient_uri: body.client_uri,\n\t\t\t\t\t\tlogo_uri: body.logo_uri,\n\t\t\t\t\t\tscope: body.scope,\n\t\t\t\t\t\tcontacts: body.contacts,\n\t\t\t\t\t\ttos_uri: body.tos_uri,\n\t\t\t\t\t\tpolicy_uri: body.policy_uri,\n\t\t\t\t\t\tjwks_uri: body.jwks_uri,\n\t\t\t\t\t\tjwks: body.jwks,\n\t\t\t\t\t\tsoftware_id: body.software_id,\n\t\t\t\t\t\tsoftware_version: body.software_version,\n\t\t\t\t\t\tsoftware_statement: body.software_statement,\n\t\t\t\t\t\tmetadata: body.metadata,\n\t\t\t\t\t\t...(clientType !== \"public\"\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tclient_secret: finalClientSecret,\n\t\t\t\t\t\t\t\t\tclient_secret_expires_at: 0, // 0 means it doesn't expire\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t};\n\n\t\t\t\t\treturn new Response(JSON.stringify(responseData), {\n\t\t\t\t\t\tstatus: 201,\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\"Cache-Control\": \"no-store\",\n\t\t\t\t\t\t\tPragma: \"no-cache\",\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t),\n\t\t\tgetMcpSession: createAuthEndpoint(\n\t\t\t\t\"/mcp/get-session\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\trequireHeaders: true,\n\t\t\t\t},\n\t\t\t\tasync (c) => {\n\t\t\t\t\tconst accessToken = c.headers\n\t\t\t\t\t\t?.get(\"Authorization\")\n\t\t\t\t\t\t?.replace(\"Bearer \", \"\");\n\t\t\t\t\tif (!accessToken) {\n\t\t\t\t\t\tc.headers?.set(\"WWW-Authenticate\", \"Bearer\");\n\t\t\t\t\t\treturn c.json(null);\n\t\t\t\t\t}\n\t\t\t\t\tconst accessTokenData =\n\t\t\t\t\t\tawait c.context.adapter.findOne<OAuthAccessToken>({\n\t\t\t\t\t\t\tmodel: modelName.oauthAccessToken,\n\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tfield: \"accessToken\",\n\t\t\t\t\t\t\t\t\tvalue: accessToken,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t});\n\t\t\t\t\tif (!accessTokenData) {\n\t\t\t\t\t\treturn c.json(null);\n\t\t\t\t\t}\n\t\t\t\t\treturn c.json(accessTokenData);\n\t\t\t\t},\n\t\t\t),\n\t\t},\n\t\tschema,\n\t\toptions,\n\t} satisfies BetterAuthPlugin;\n};\n\nexport const withMcpAuth = <\n\tAuth extends {\n\t\tapi: {\n\t\t\tgetMcpSession: (...args: any) => Promise<OAuthAccessToken | null>;\n\t\t};\n\t\toptions: BetterAuthOptions;\n\t},\n>(\n\tauth: Auth,\n\thandler: (\n\t\treq: Request,\n\t\tsession: OAuthAccessToken,\n\t) => Response | Promise<Response>,\n) => {\n\treturn async (req: Request) => {\n\t\tconst baseURL = getBaseURL(auth.options.baseURL, auth.options.basePath);\n\t\tif (!baseURL && !isProduction) {\n\t\t\tlogger.warn(\"Unable to get the baseURL, please check your config!\");\n\t\t}\n\t\tconst session = await auth.api.getMcpSession({\n\t\t\theaders: req.headers,\n\t\t});\n\t\tconst wwwAuthenticateValue = `Bearer resource_metadata=\"${baseURL}/.well-known/oauth-protected-resource\"`;\n\t\tif (!session) {\n\t\t\treturn Response.json(\n\t\t\t\t{\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: -32000,\n\t\t\t\t\t\tmessage: \"Unauthorized: Authentication required\",\n\t\t\t\t\t\t\"www-authenticate\": wwwAuthenticateValue,\n\t\t\t\t\t},\n\t\t\t\t\tid: null,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tstatus: 401,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"WWW-Authenticate\": wwwAuthenticateValue,\n\t\t\t\t\t\t// we also add this headers otherwise browser based clients will not be able to read the `www-authenticate` header\n\t\t\t\t\t\t\"Access-Control-Expose-Headers\": \"WWW-Authenticate\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn handler(req, session);\n\t};\n};\n\nexport const oAuthDiscoveryMetadata = <\n\tAuth extends {\n\t\tapi: {\n\t\t\tgetMcpOAuthConfig: (...args: any) => any;\n\t\t};\n\t},\n>(\n\tauth: Auth,\n) => {\n\treturn async (request: Request) => {\n\t\tconst res = await auth.api.getMcpOAuthConfig();\n\t\treturn new Response(JSON.stringify(res), {\n\t\t\tstatus: 200,\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\"Access-Control-Allow-Origin\": \"*\",\n\t\t\t\t\"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n\t\t\t\t\"Access-Control-Allow-Headers\": \"Content-Type, Authorization\",\n\t\t\t\t\"Access-Control-Max-Age\": \"86400\",\n\t\t\t},\n\t\t});\n\t};\n};\n\nexport const oAuthProtectedResourceMetadata = <\n\tAuth extends {\n\t\tapi: {\n\t\t\tgetMCPProtectedResource: (...args: any) => any;\n\t\t};\n\t},\n>(\n\tauth: Auth,\n) => {\n\treturn async (request: Request) => {\n\t\tconst res = await auth.api.getMCPProtectedResource();\n\t\treturn new Response(JSON.stringify(res), {\n\t\t\tstatus: 200,\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\"Access-Control-Allow-Origin\": \"*\",\n\t\t\t\t\"Access-Control-Allow-Methods\": \"POST, OPTIONS\",\n\t\t\t\t\"Access-Control-Allow-Headers\": \"Content-Type, Authorization\",\n\t\t\t\t\"Access-Control-Max-Age\": \"86400\",\n\t\t\t},\n\t\t});\n\t};\n};\n"],"mappings":"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