( dbHandle: SelfHostDbHandle, )
| 36 | } |
| 37 | |
| 38 | export const resolveAuthProviders = async ( |
| 39 | dbHandle: SelfHostDbHandle, |
| 40 | ): Promise<ResolvedAuthProviders> => { |
| 41 | const betterAuth = await buildBetterAuth(dbHandle.client); |
| 42 | const betterAuthLayer = Layer.succeed(BetterAuth)(betterAuth); |
| 43 | |
| 44 | // The consent redirect from Better Auth's authorize only carries the opaque |
| 45 | // client_id; look the registered client_name up (its adapter sees the |
| 46 | // just-written DCR row) so the approval screen reads "Connect Codex?" not a |
| 47 | // random id. Self-declared at open DCR — a label, not a trust signal. |
| 48 | const lookupClientName = async (clientId: string): Promise<string | null> => { |
| 49 | const ctx = await betterAuth.auth.$context; |
| 50 | const app = await ctx.adapter.findOne<{ name?: string | null }>({ |
| 51 | model: "oauthApplication", |
| 52 | where: [{ field: "clientId", value: clientId }], |
| 53 | }); |
| 54 | return app?.name ?? null; |
| 55 | }; |
| 56 | |
| 57 | // Force the MCP approval screen: inject `prompt=consent` on every MCP |
| 58 | // authorize so a connecting client is gated on /mcp-consent rather than |
| 59 | // silently granted a token (see ./force-mcp-consent), and enrich the |
| 60 | // resulting consent redirect with the registered client name. |
| 61 | const config = loadConfig(); |
| 62 | const authHandler = async (request: Request): Promise<Response> => { |
| 63 | const response = await betterAuth.handler(withForcedMcpConsent(request)); |
| 64 | // Turn Better Auth's bare 403 "Invalid origin" into a setup instruction — |
| 65 | // on a fresh deploy it almost always means the public URL needs configuring. |
| 66 | const friendlier = await rewriteInvalidOrigin(request, response, config.webBaseUrl); |
| 67 | if (friendlier) return friendlier; |
| 68 | if (response.status !== 302) return response; |
| 69 | const clientId = consentRedirectClientId(response.headers.get("location")); |
| 70 | if (!clientId) return response; |
| 71 | const name = await lookupClientName(clientId); |
| 72 | if (!name) return response; |
| 73 | // Preserve the rest of the response — notably the signed consent cookie. |
| 74 | const headers = new Headers(response.headers); |
| 75 | headers.set("location", withClientName(response.headers.get("location")!, name)); |
| 76 | return new Response(null, { status: 302, headers }); |
| 77 | }; |
| 78 | |
| 79 | return { |
| 80 | identityLayer: betterAuthIdentityLayer.pipe(Layer.provide(betterAuthLayer)), |
| 81 | authHandler, |
| 82 | betterAuth, |
| 83 | }; |
| 84 | }; |
no test coverage detected