From 194e6bd82a43bd87132563948c25883de0ca2bf7 Mon Sep 17 00:00:00 2001 From: rgarcia <72655+rgarcia@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:07:21 +0000 Subject: [PATCH] Advertise dev OAuth issuer from MCP discovery --- src/lib/oauth-discovery-route.test.ts | 1 + src/lib/oauth-discovery.test.ts | 35 +++++++++++++++++++++++---- src/lib/oauth-discovery.ts | 10 ++++++-- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/lib/oauth-discovery-route.test.ts b/src/lib/oauth-discovery-route.test.ts index ae8bacf..db92eca 100644 --- a/src/lib/oauth-discovery-route.test.ts +++ b/src/lib/oauth-discovery-route.test.ts @@ -17,6 +17,7 @@ const { GET, OPTIONS } = await import( describe("/.well-known/oauth-protected-resource/mcp", () => { test.each([ ["https://mcp.onkernel.com", "https://auth.onkernel.com"], + ["https://mcp.dev.onkernel.com", "https://auth.dev.onkernel.com"], ["http://localhost:3002", "http://localhost:3002"], ["https://mcp-staging.onkernel.com", "https://mcp-staging.onkernel.com"], ])("serves uncached metadata bound to %s/mcp", async (origin, issuer) => { diff --git a/src/lib/oauth-discovery.test.ts b/src/lib/oauth-discovery.test.ts index 7b277e7..874acf2 100644 --- a/src/lib/oauth-discovery.test.ts +++ b/src/lib/oauth-discovery.test.ts @@ -8,6 +8,11 @@ describe("OAuth protected-resource discovery", () => { it.each([ ["https://mcp.onkernel.com", undefined, "https://mcp.onkernel.com"], ["http://localhost:3002", "mcp.onkernel.com", "https://mcp.onkernel.com"], + [ + "http://localhost:3002", + "mcp.dev.onkernel.com", + "https://mcp.dev.onkernel.com", + ], ["http://localhost:3002", undefined, "http://localhost:3002"], [ "https://localhost:3000", @@ -66,26 +71,45 @@ describe("OAuth protected-resource discovery", () => { }); }); - it("pins resource and issuer when Next uses an internal request origin", () => { + it.each([ + ["mcp.onkernel.com", "auth.onkernel.com"], + ["mcp.dev.onkernel.com", "auth.dev.onkernel.com"], + ])("pins %s discovery to %s behind Next", (resourceHost, issuerHost) => { const metadata = oauthResourceMetadata( new Request( "https://localhost:3002/.well-known/oauth-protected-resource/mcp", { headers: { - Host: "mcp.onkernel.com", + Host: resourceHost, "X-Forwarded-Host": "evil.example", }, }, ), {}, ); - expect(metadata.resource).toBe("https://mcp.onkernel.com/mcp"); + expect(metadata).toMatchObject({ + resource: `https://${resourceHost}/mcp`, + authorization_servers: [`https://${issuerHost}`], + authorization_endpoint: `https://${issuerHost}/authorize`, + token_endpoint: `https://${issuerHost}/token`, + registration_endpoint: `https://${issuerHost}/register`, + }); + }); + + it("advertises the dev OAuth server on direct dev requests", () => { + const metadata = oauthResourceMetadata( + new Request( + "https://mcp.dev.onkernel.com/.well-known/oauth-protected-resource/mcp", + ), + {}, + ); + expect(metadata.resource).toBe("https://mcp.dev.onkernel.com/mcp"); expect(metadata.authorization_servers).toEqual([ - "https://auth.onkernel.com", + "https://auth.dev.onkernel.com", ]); }); - it("uses the public Host for non-production discovery behind Next", () => { + it("uses the public Host for other non-production discovery behind Next", () => { for (const host of [ "mcp-staging.onkernel.com", "preview.example", @@ -114,6 +138,7 @@ describe("OAuth protected-resource discovery", () => { "https://mcp-staging.onkernel.com", "https://preview.example", "https://mcp.onkernel.com.evil.example", + "https://mcp.dev.onkernel.com.evil.example", ]) { const metadata = oauthResourceMetadata( new Request(`${origin}/.well-known/oauth-protected-resource/mcp`), diff --git a/src/lib/oauth-discovery.ts b/src/lib/oauth-discovery.ts index dc7bfdc..0d94bce 100644 --- a/src/lib/oauth-discovery.ts +++ b/src/lib/oauth-discovery.ts @@ -1,5 +1,7 @@ const MCP_ORIGIN = "https://mcp.onkernel.com"; const OAUTH_ORIGIN = "https://auth.onkernel.com"; +const DEV_MCP_ORIGIN = "https://mcp.dev.onkernel.com"; +const DEV_OAUTH_ORIGIN = "https://auth.dev.onkernel.com"; export const OAUTH_RESOURCE_METADATA_PATH = "/.well-known/oauth-protected-resource/mcp"; @@ -11,7 +13,9 @@ function mcpOrigin(request: Request): string { url.port = ""; url.host = host; } - return url.host === "mcp.onkernel.com" ? MCP_ORIGIN : url.origin; + if (url.host === "mcp.onkernel.com") return MCP_ORIGIN; + if (url.host === "mcp.dev.onkernel.com") return DEV_MCP_ORIGIN; + return url.origin; } export function oauthResourceMetadataUrl(request: Request): string { @@ -23,7 +27,9 @@ export function oauthResourceMetadata( clerkMetadata: Record, ): Record { const origin = mcpOrigin(request); - const authorizationServer = origin === MCP_ORIGIN ? OAUTH_ORIGIN : origin; + let authorizationServer = origin; + if (origin === MCP_ORIGIN) authorizationServer = OAUTH_ORIGIN; + if (origin === DEV_MCP_ORIGIN) authorizationServer = DEV_OAUTH_ORIGIN; return { ...clerkMetadata,