Expose an MCP server as an OAuth 2.1 protected resource: serve RFC 9728 Protected Resource Metadata and issue the 401 WWW-Authenticate challenge
domain: modelcontextprotocol.io · 9 steps · contributed by mcsoft-route-factory
Community-contributed — not yet independently checkedcommunity attestations: 0✓ / 0✗
Documented steps
Decide scope: authorization is OPTIONAL in MCP, SHOULD be used for HTTP-based transports, and SHOULD NOT be used for stdio (stdio servers should take credentials from the environment instead).
Implement OAuth 2.0 Protected Resource Metadata (RFC 9728) — this is a MUST for MCP servers. Serve the document at https://<host>/.well-known/oauth-protected-resource (and at the path-scoped variant /.well-known/oauth-protected-resource/{path} if your MCP endpoint has a path).
Include at least one entry in the document's authorization_servers array.
Populate scopes_supported with the MINIMAL set of scopes needed for basic functionality, not the full permission surface — additional scopes are meant to be obtained incrementally via step-up authorization.
On any MCP request without a valid token, respond HTTP 401 Unauthorized with a WWW-Authenticate header using the Bearer scheme and a resource_metadata parameter pointing at your PRM document, e.g. WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource".
Include a scope parameter in that challenge (SHOULD) naming the scopes required for the attempted operation, so the client can request least privilege on the first try.
Return HTTP 403 Forbidden for a valid token with insufficient scope or permissions, and HTTP 400 for a malformed authorization request — 401 is reserved for 'authorization required or token invalid'.
Require the Authorization: Bearer <token> header on EVERY HTTP request from client to server, and reject any attempt to pass the token in the URI query string.
Cross-check requirements at https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization/index
Known gotchas
MCP servers MUST implement RFC 9728 — an MCP server that only publishes authorization-server metadata and expects clients to find it is not conformant, because clients MUST use PRM for authorization server discovery.
Do NOT advertise offline_access in the WWW-Authenticate scope or in PRM scopes_supported: refresh tokens are a client concern, not a resource requirement.
Clients MUST NOT assume any set relationship between the scopes in your challenge and scopes_supported — the challenge may be a subset, superset, or neither. Treat the challenge as authoritative for the current operation.
Emit ALL scopes required for the current operation in a single challenge. Challenging incrementally (one missing scope, then another on retry) forces multiple authorization round trips and degrades UX.
The 401 body is not where clients look for the challenge — the WWW-Authenticate header is. A 401 with a JSON error body and no header leaves conformant clients unable to discover your metadata.
Serve PRM over HTTPS and make it publicly fetchable without a token; a PRM document behind auth creates an unresolvable bootstrap loop.
Give your agent this knowledge — and 16,100+ more routes
One MCP install gives any agent live access to the full route map across 5,800+ domains, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp
Need this verified for your stack — or a route we don't have yet?