Return JSON 404 for OAuth discovery when auth is off#4366
Return JSON 404 for OAuth discovery when auth is off#4366
Conversation
There was a problem hiding this comment.
Large PR Detected
This PR exceeds 1000 lines of changes and requires justification before it can be reviewed.
How to unblock this PR:
Add a section to your PR description with the following format:
## Large PR Justification
[Explain why this PR must be large, such as:]
- Generated code that cannot be split
- Large refactoring that must be atomic
- Multiple related changes that would break if separated
- Migration or data transformationAlternative:
Consider splitting this PR into smaller, focused changes (< 1000 lines each) for easier review and reduced risk.
See our Contributing Guidelines for more details.
This review will be automatically dismissed once you add the justification section.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4366 +/- ##
==========================================
+ Coverage 69.55% 69.61% +0.05%
==========================================
Files 480 480
Lines 48837 48838 +1
==========================================
+ Hits 33971 34000 +29
+ Misses 12249 12220 -29
- Partials 2617 2618 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Claude Code v2.1.83 sends GET /.well-known/oauth-protected-resource
before connecting to any HTTP MCP server. When auth is not configured,
no .well-known handler was registered, so the request fell through to
the MCP catch-all handler whose headerValidatingMiddleware rejected it
with HTTP 406 and a JSON-RPC error body (where "error" is an object).
Claude Code expects "error" as a string for OAuth errors, causing Zod
validation failure and a spurious "needs authentication" prompt.
Always register a .well-known prefix handler. When AuthInfoHandler is
nil, it returns HTTP 404 with {"error":"not_found"} — a clean signal
that no auth metadata exists and the client should proceed without
authentication.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d4f5b5d to
0b99d31
Compare
PR size has been reduced below the XL threshold. Thank you for splitting this up!
|
✅ PR size has been reduced below the XL threshold. The size review has been dismissed and this PR can now proceed with normal review. Thank you for splitting this up! |
Summary
Claude Code v2.1.83 introduced mandatory OAuth discovery for all HTTP MCP servers — it sends
GET /.well-known/oauth-protected-resourcebefore connecting. When the vMCP server (or transparent proxy) has no auth configured, no.well-knownhandler was registered, so these requests fell through to the/catch-all MCP handler. There,headerValidatingMiddlewarerejected the GET with HTTP 406 and a JSON-RPC error body where"error"is an object. Claude Code expects"error"as a string (OAuth error format), Zod validation fails, and it shows "needs authentication" even though no auth is needed..well-known/prefix handler viaNewWellKnownHandler, even whenAuthInfoHandleris nil{"error":"not_found"}JSON — a clean, parseable signal that no auth metadata existsType of change
Test plan
task test)task lint-fix)Verified that brood-box (which uses the vMCP proxy without auth) builds with a local
replacedirective and Claude Code v2.1.83 connects without the spurious "needs authentication" prompt.Changes
pkg/auth/well_known.goNewWellKnownHandleralways returns a handler; nil authInfoHandler → 404 JSONpkg/auth/well_known_test.gopkg/vmcp/server/server.go.well-known/prefix unconditionallypkg/transport/proxy/transparent/transparent_proxy.goSpecial notes for reviewers
The root cause is that
headerValidatingMiddleware(which guards the MCP handler) returns a JSON-RPC error with"error"as an object, but OAuth discovery clients expect"error"as a string per RFC 6749. Rather than changing the middleware's error format (which is correct for MCP), we prevent OAuth discovery from reaching it at all.Generated with Claude Code