Skip to content

Commit bb53848

Browse files
authored
Merge pull request #96 from kernel/fix/restore-oauth-as-metadata
fix: restore OAuth AS metadata endpoint for Claude Code DCR flow
2 parents feb076f + 071b81e commit bb53848

3 files changed

Lines changed: 69 additions & 13 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { NextRequest } from "next/server";
2+
3+
const CORS_HEADERS = {
4+
"Access-Control-Allow-Origin": "*",
5+
"Access-Control-Allow-Methods": "GET, OPTIONS",
6+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
7+
};
8+
9+
export async function OPTIONS(): Promise<Response> {
10+
return new Response(null, { status: 204, headers: CORS_HEADERS });
11+
}
12+
13+
export async function GET(request: NextRequest): Promise<Response> {
14+
const clerkDomain = process.env.NEXT_PUBLIC_CLERK_DOMAIN;
15+
16+
if (!clerkDomain) {
17+
return Response.json(
18+
{ error: "server_error", error_description: "Clerk domain not found" },
19+
{ status: 500 },
20+
);
21+
}
22+
23+
const baseUrl = `${request.nextUrl.protocol}//${request.nextUrl.host}`;
24+
25+
const metadata = {
26+
issuer: baseUrl,
27+
authorization_endpoint: `${baseUrl}/authorize`,
28+
token_endpoint: `${baseUrl}/token`,
29+
registration_endpoint: `${baseUrl}/register`,
30+
jwks_uri: `https://${clerkDomain}/.well-known/jwks.json`,
31+
scopes_supported: ["openid"],
32+
response_types_supported: ["code"],
33+
grant_types_supported: ["authorization_code", "refresh_token"],
34+
code_challenge_methods_supported: ["S256"],
35+
token_endpoint_auth_methods_supported: [
36+
"none",
37+
"client_secret_post",
38+
"client_secret_basic",
39+
],
40+
};
41+
42+
return Response.json(metadata, { headers: CORS_HEADERS });
43+
}

src/app/.well-known/oauth-protected-resource/mcp/route.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,14 @@ const handler = async (request: NextRequest) => {
99
const clerkMetadata = await clerkResponse.json();
1010

1111
const baseUrl = `${request.nextUrl.protocol}//${request.nextUrl.host}`;
12-
const clerkDomain = process.env.NEXT_PUBLIC_CLERK_DOMAIN;
13-
14-
if (!clerkDomain) {
15-
return Response.json(
16-
{ error: "server_error", error_description: "Clerk domain not found" },
17-
{ status: 500 },
18-
);
19-
}
20-
21-
const clerkBaseUrl = `https://${clerkDomain}`;
2212

2313
const modifiedMetadata: Record<string, unknown> = {
2414
...clerkMetadata,
2515
resource: baseUrl,
2616
authorization_servers: [baseUrl],
2717
authorization_endpoint: `${baseUrl}/authorize`,
2818
token_endpoint: `${baseUrl}/token`,
29-
registration_endpoint: `${clerkBaseUrl}/oauth/register`,
19+
registration_endpoint: `${baseUrl}/register`,
3020
scopes_supported: ["openid"],
3121
};
3222

src/app/token/route.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,31 @@ export async function POST(request: NextRequest): Promise<NextResponse> {
8888
const grantType = body.get("grant_type") as string;
8989
console.debug("[token] start", { grantType });
9090

91-
// Validate client_id (required for both flows)
92-
const clientId = body.get("client_id") as string | null;
91+
// Extract client_id from body or Authorization: Basic header
92+
let clientId = body.get("client_id") as string | null;
93+
let clientSecret = body.get("client_secret") as string | null;
94+
95+
if (!clientId) {
96+
const authHeader = request.headers.get("authorization");
97+
if (authHeader?.startsWith("Basic ")) {
98+
try {
99+
const decoded = atob(authHeader.slice(6));
100+
const colonIdx = decoded.indexOf(":");
101+
if (colonIdx !== -1) {
102+
clientId = decodeURIComponent(decoded.slice(0, colonIdx));
103+
clientSecret = decodeURIComponent(decoded.slice(colonIdx + 1));
104+
params.set("client_id", clientId);
105+
if (clientSecret) {
106+
params.set("client_secret", clientSecret);
107+
}
108+
console.debug("[token] extracted client_id from Basic auth header");
109+
}
110+
} catch {
111+
console.debug("[token] failed to decode Basic auth header");
112+
}
113+
}
114+
}
115+
93116
if (!clientId) {
94117
console.debug("[token] missing client_id");
95118
return createErrorResponse(

0 commit comments

Comments
 (0)