Skip to content

feature: Support OAuth Protected Resource Metadata discovery via WWW-Authenticate header #688

@qhzhu

Description

@qhzhu

Problem Statement

When an MCP server is deployed at a sub-path (e.g., [https://gateway.example.com/mcp](https://gateway.example.com/mcp)), the current OAuth implementation fails to discover the Protected Resource Metadata (PRM) correctly.

The client constructs an origin-based URL:

https://gateway.example.com/.well-known/oauth-protected-resource 

But the server can only serve PRM at:

https://gateway.example.com/mcp/.well-known/oauth-protected-resource 

This is a common scenario when:

  • MCP server is deployed behind a shared API gateway
  • Multiple services share the same domain with different paths
  • Cloud platforms or Kubernetes Ingress route traffic by path
  • The service owner doesn't control the root path of the domain

Proposed Solution

Parse the resource_metadata parameter from the WWW-Authenticate header when receiving a 401 Unauthorized response, and use this URL for PRM discovery instead of constructing an origin-based URL.

Implementation approach:

  1. When receiving 401 Unauthorized, parse resource_metadata from WWW-Authenticate header
  2. Store this URL in the OAuthHandler
  3. Use this URL (if present) in getServerMetadata() before falling back to origin-based discovery
  4. Maintain full backward compatibility - if no resource_metadata is provided, current behavior is preserved

MCP Spec Reference

According to the MCP Authorization spec, when a server returns 401 Unauthorized, it should include:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource"

The client should parse this header and use the provided URL for PRM discovery. See also RFC 9728 - OAuth 2.0 Protected Resource Metadata.

Example Usage

// No API changes required - the fix is internal to the OAuth discovery flow.
// After the fix, this would work correctly for sub-path deployments:

client, _ := client.NewOAuthStreamableHttpClient(
    "https://gateway.example.com/mcp",  // MCP server at sub-path
    oauthConfig,
)

// Client receives 401 with:
//   WWW-Authenticate: Bearer resource_metadata="https://gateway.example.com/mcp/.well-known/oauth-protected-resource"
// 
// Client automatically uses the provided URL for PRM discovery 

Alternatives/Workarounds Considered

  1. Manually set AuthServerMetadataURL - Works but requires users to know the OIDC discovery URL upfront, bypassing the standard discovery flow.

  2. Deploy PRM at origin root - Not always possible when the service owner doesn't control the root path.


Notes

I have a local branch that implemented the fix/enhancement, and I'd be happy to contribute if it would be helpful - please let me know if opening a new PR would be appropriate. This is my first contribution to this project, so any guidance would be greatly appreciated!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions