Skip to content

fix: restore OAuth AS metadata endpoint for Claude Code DCR flow#96

Merged
masnwilliams merged 4 commits intomainfrom
fix/restore-oauth-as-metadata
Apr 15, 2026
Merged

fix: restore OAuth AS metadata endpoint for Claude Code DCR flow#96
masnwilliams merged 4 commits intomainfrom
fix/restore-oauth-as-metadata

Conversation

@masnwilliams
Copy link
Copy Markdown
Collaborator

@masnwilliams masnwilliams commented Apr 14, 2026

Summary

Fixes MCP OAuth flow for Claude Code (and likely other MCP clients that use client_secret_basic auth).

Two bugs fixed:

  1. Token endpoint doesn't handle Basic auth — Our /register (DCR) returns a client_secret (Clerk returns one even for public: true apps). Claude Code uses client_secret_basic auth method, sending Authorization: Basic base64(client_id:client_secret) on the token exchange. Our /token route only read client_id from the form body → 400 "missing client_id" on every token exchange. Customers complete the entire OAuth flow and silently fail at the last step, ending up with accessToken: "".

  2. Missing /.well-known/oauth-authorization-server endpoint — Accidentally deleted in 9991d7e (Aug 2025 refactor). Causes oauthMetadataFound: false in Claude Code. Not the direct cause of failure (Claude Code falls back to path guessing), but spec-noncompliant and may break other MCP clients.

Also fixed: registration_endpoint in protected-resource metadata pointed at Clerk's /oauth/register (which defaults to email+profile+offline scopes) instead of our /register (which enforces openid-only).

Customer impact

Any user connecting Claude Code to the Kernel MCP server via OAuth would hit this. DCR succeeds, authorization completes, but the token exchange silently fails. The credential keychain shows accessToken: "", expiresAt: 0.

Workaround for existing users: After deploying this fix, customers need to clear stale credentials:

security delete-generic-password -s "Claude Code-credentials"

Then reconnect to the MCP server.

Changes

  • src/app/token/route.ts — Extract client_id/client_secret from Authorization: Basic header when not present in form body
  • src/app/.well-known/oauth-authorization-server/route.ts — Restore RFC 8414 AS metadata endpoint with our proxied endpoints
  • src/app/.well-known/oauth-protected-resource/mcp/route.ts — Point registration_endpoint at our /register instead of Clerk's

Test plan

  • POST /token with Basic auth header returns 200 (verified on mcp.dev.onkernel.com)
  • GET /.well-known/oauth-authorization-server returns valid metadata
  • GET /.well-known/oauth-protected-resource/mcp shows registration_endpoint → our /register
  • Full Claude Code OAuth flow completes successfully on preview deployment

Note

Medium Risk
Touches OAuth discovery and /token exchange logic; while changes are small, parsing Authorization: Basic and altering metadata endpoints can impact interoperability and auth flows if incorrect.

Overview
Restores the RFC 8414 authorization server discovery endpoint at /.well-known/oauth-authorization-server, returning proxied issuer/endpoint metadata with CORS enabled.

Fixes MCP protected-resource metadata to advertise the local /register DCR endpoint (instead of Clerk’s), aligning the registration flow with openid-only expectations.

Updates /token to accept client_secret_basic by extracting client_id/client_secret from the Authorization: Basic header when missing from the form body and forwarding them to the Clerk token exchange.

Reviewed by Cursor Bugbot for commit 071b81e. Bugbot is set up for automated code reviews on this repo. Configure here.

The /.well-known/oauth-authorization-server route was accidentally
deleted in 9991d7e, causing Claude Code to set oauthMetadataFound:false
and fail the token exchange after DCR. Restoring it with our proxied
endpoints (not Clerk's directly) so the full OAuth 2.1 flow completes.

Also fixes registration_endpoint in the protected-resource metadata
to point at our /register (which enforces openid-only scope) instead
of Clerk's /oauth/register (which defaults to email+profile+offline).

Made-with: Cursor
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
kernel-mcp-server Error Error Apr 14, 2026 11:34pm
mcp Ready Ready Preview, Comment Apr 14, 2026 11:34pm
mcp (staging) Ready Ready Preview, Comment Apr 14, 2026 11:34pm

Comment thread src/app/.well-known/oauth-protected-resource/mcp/route.ts
@firetiger-agent
Copy link
Copy Markdown

I'll monitor this deployment which adds the /.well-known/oauth-authorization-server endpoint and updates the registration endpoint routing.

What I'll watch for:

  • Deployment completes successfully on Vercel
  • The new OAuth discovery endpoint returns valid metadata (HTTP 200)
  • No increase in errors from the modified /.well-known/oauth-protected-resource/mcp endpoint
  • Any downstream effects on Kernel API if MCP OAuth flows are impacted

Note: The kernel-mcp-server doesn't emit telemetry to our observability platform, so monitoring is limited to deployment status and downstream effects. I'll verify the deployment succeeds and watch for any anomalies in the main Kernel API that could indicate MCP auth issues. This is a low-risk change with straightforward rollback if needed.

View agent

Claude Code sends client_id and client_secret via HTTP Basic Auth
(Authorization: Basic base64(client_id:client_secret)) when DCR
returns a client_secret. Our /token route only checked the form
body, causing "missing client_id" 400 errors on every token exchange.

Made-with: Cursor
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 071b81e. Configure here.

Comment thread src/app/token/route.ts
const colonIdx = decoded.indexOf(":");
if (colonIdx !== -1) {
clientId = decodeURIComponent(decoded.slice(0, colonIdx));
clientSecret = decodeURIComponent(decoded.slice(colonIdx + 1));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basic auth decoding mishandles form-encoded + as space

Low Severity

RFC 6749 Section 2.3.1 requires client_id and client_secret to be application/x-www-form-urlencoded before base64-encoding in the Basic auth header. Form encoding converts spaces to +, but decodeURIComponent does not decode + back to space — it only handles %XX sequences. If a credential ever contains a space, + would be kept literally instead of being decoded. The correct approach is to replace + with space before calling decodeURIComponent.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 071b81e. Configure here.

@masnwilliams masnwilliams requested a review from ehfeng April 14, 2026 23:41
Copy link
Copy Markdown

@ehfeng ehfeng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@masnwilliams masnwilliams merged commit bb53848 into main Apr 15, 2026
9 checks passed
@masnwilliams masnwilliams deleted the fix/restore-oauth-as-metadata branch April 15, 2026 05:40
@firetiger-agent
Copy link
Copy Markdown

Monitoring Complete - Successful

The deployment to Production has completed successfully with no issues detected.

Summary:

  • Deployment confirmed successful via GitHub deployment status
  • No authentication-related errors or anomalies detected in downstream Kernel API monitoring
  • No increase in failures that would indicate broken OAuth flows
  • Error rates remained stable at baseline (0.06-0.08%)

Note on verification: As identified in the monitoring plan, the kernel-mcp-server doesn't emit telemetry to our observability platform. We monitored for downstream effects on the main Kernel API (which would surface if MCP OAuth flows broke), and found none. Manual verification of the new /.well-known/oauth-authorization-server endpoint can confirm it returns the expected metadata if desired.

This change appears to be working correctly.

View agent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants