Skip to content

[BUG]: OAuth 401 responses missing resource_metadata in WWW-Authenticate when server requires OAuth #3990

@ecthelion77

Description

@ecthelion77

🐞 Bug Summary

When a request arrives without an OAuth token and the backend MCP server requires OAuth authentication, the 401 response's WWW-Authenticate header is missing the resource_metadata attribute. This breaks MCP OAuth discovery for clients that rely on this attribute to locate the resource metadata endpoint (per the MCP OAuth specification).

The root cause is a code ordering issue in _auth_no_token(): the strict-mode and empty-bearer checks return 401 before _check_server_oauth_enforcement() is called, so the resource_metadata URL is never included in the response.


🧩 Affected Component

  • Federation or Transports

🔁 Steps to Reproduce

  1. Configure a gateway with a backend MCP server that has OAuth enabled
  2. Send a request to a tool on that server without an Authorization header
  3. Observe the 401 response's WWW-Authenticate header
  4. The header contains Bearer realm="mcp-gateway" but is missing the resource_metadata attribute

🤔 Expected Behavior

The 401 WWW-Authenticate header should include the resource_metadata attribute pointing to the server's OAuth resource metadata endpoint, e.g.:

Bearer realm="mcp-gateway", resource_metadata="https://gateway.example.com/.well-known/oauth-protected-resource/server-name"

This allows MCP clients to discover the OAuth configuration and initiate the authorization flow.


📓 Logs / Error Output

No crash — the gateway returns 401 but with an incomplete WWW-Authenticate header. Clients that follow the MCP OAuth spec cannot discover the resource metadata and fail to authenticate.


🧠 Environment Info

Key Value
Version or commit 1.0.0-RC2
Runtime Python 3.12, Gunicorn
Platform / OS Linux (Kubernetes)
Container Docker

🧩 Additional Context

The fix reorders _auth_no_token() in streamablehttp_transport.py:

  1. Call _check_server_oauth_enforcement() first to determine if the server requires OAuth
  2. Build the www_auth string with the resource_metadata attribute when applicable
  3. Use the enriched www_auth in all subsequent 401 responses (strict-mode check, empty-bearer check)

This ensures every 401 response includes the full OAuth discovery information regardless of which check triggers the rejection.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions