MCP OAuth client invents `<gateway-origin>/authorize` instead of resolving the RFC 9728 discovery chain (authorization_servers → AS metadata) — non-compliant with MCP auth spec
Environment
- Claude Code 2.1.274, macOS
- Remote MCP server behind an API gateway (HTTP transport), authorization server = Okta (
https://idp.example.com/oauth2/default)
Note: this is the same class of defect as #80731 (closed as stale without a fix), #85563 and #66456. Re-filing with a fully curl-verifiable reproduction, because the gateway-side metadata is provably RFC 9728-compliant — the bug is entirely in Claude Code's client-side discovery resolution.
Summary
When the MCP server returns a 401 challenge with WWW-Authenticate: Bearer resource_metadata="...", Claude Code never resolves the protected-resource metadata and never follows authorization_servers to fetch the authorization server's metadata. Instead it fabricates an authorize endpoint on the resource server's own origin (https://<gateway-host>/authorize?...), which 404s.
This violates:
- RFC 9728 §3.1 — protected resource metadata's
authorization_serversfield exists precisely so the resource can advertise its AS(s); the client MUST use it rather than assuming the resource origin is the AS. - The MCP authorization specification (2025-06-18 and later drafts) — which normatively requires clients to implement RFC 9728 discovery from the
WWW-Authenticatechallenge on 401, then perform AS metadata discovery (RFC 8414) on the returned issuer. - RFC 8414 §3.1 — the
authorization_endpointused in the redirect MUST come from the AS's well-known metadata, not be constructed by the client.
Reproduction (all steps verified with curl — gateway config is internally consistent)
- Client sends an unauthenticated request to the MCP route:
````
POST https://mcp.example.com/mcp/datadog
- Gateway correctly answers with a spec-compliant challenge:
````
HTTP/1.1 401
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp"
- That metadata document resolves correctly:
``json``
{
"resource": "https://mcp.example.com/mcp/datadog",
"authorization_servers": ["https://idp.example.com/oauth2/default"]
}
- The advertised AS's real metadata resolves correctly too:
````
https://idp.example.com/oauth2/default/.well-known/openid-configuration
→ authorization_endpoint: https://idp.example.com/oauth2/default/v1/authorize
→ token_endpoint: https://idp.example.com/oauth2/default/v1/token
Expected
Claude Code:
- Parses the
WWW-Authenticatechallenge and fetchesresource_metadata(RFC 9728 §5). - Follows
authorization_servers[0]and fetches the AS metadata (RFC 8414 / OIDC discovery). - Redirects the browser to the discovered
authorization_endpoint(Okta) and redeems the code at the discoveredtoken_endpoint.
Actual
Claude Code constructs https://mcp.example.com/authorize?... — an endpoint that exists nowhere in any metadata document — and sends the browser there, which 404s. No request is ever made to the protected-resource metadata URL or to the Okta issuer's discovery document.
Impact
Any correctly-implemented MCP deployment with a gateway/proxy as resource server and a separate IdP as authorization server (Okta, Entra ID, Cognito, Auth0, Keycloak — the single most common enterprise topology) cannot be authenticated by Claude Code at all. The failure is silent and looks like a server-side 404, which sends users debugging the wrong component.
Suggested fix
In the MCP OAuth client flow:
- On 401, extract
resource_metadatafrom theWWW-Authenticateheader and fetch it (falling back to RFC 9728 well-known path insertion only if the header is absent). - Resolve the AS from
authorization_servers[0]of that document. - Run standard issuer metadata discovery (
.well-known/openid-configuration, then RFC 8414 path-insertion fallback — see also #95270) and use the discoveredauthorization_endpoint/token_endpointverbatim.
Upstream fix
The root cause lives in the vendored @modelcontextprotocol/sdk client auth code, and a fix has been proposed upstream in the TypeScript SDK:
- modelcontextprotocol/typescript-sdk#2785 —
fix(client): OAuth well-known fallback on 4xx and authorization-server origin— fixes the fallback URL construction so authorization-server metadata discovery stays on the AS host instead of falling back to the resource-server origin (this issue's/authorizefabrication), and widens the 4xx fallback trigger.
Once that PR is merged, Claude Code should pick it up via an SDK dependency bump. Related SDK issues: #2784, #2866.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗