import { DocsTitle, DocsLead, DocsH2, DocsP, DocsList, DocsLi, DocsCode, Mono, } from '@/components/docs-page'; export const metadata = { title: 'OAuth 2.1 flow — BuildMyMCPServer docs' }; export default function OAuthDocs() { return ( <> OAuth 2.1 flow Every generated server is an OAuth 2.1 Resource Server. The control plane is the Authorization Server. Dynamic Client Registration, PKCE, and Resource Indicators per the 2025 MCP authorization spec. Standards we follow OAuth 2.1 draft (draft-ietf-oauth-v2-1) — no implicit, mandatory PKCE RFC 8414 — Authorization Server Metadata at /.well-known/oauth-authorization-server RFC 9728 — Protected Resource Metadata at /.well-known/oauth-protected-resource RFC 8707 — Resource Indicators (audience binding) RFC 7591 — Dynamic Client Registration End-to-end walkthrough First request from a fresh client to a fresh server is unauthenticated. The server replies with a 401 plus a WWW-Authenticate header pointing to its resource metadata. The client fetches that resource metadata, sees the authorization server, then fetches the AS metadata to discover registration, authorize, token and JWKS endpoints. The client registers itself dynamically. No human in the loop, no preconfigured client IDs. Each AI surface gets its own ephemeral identity. Authorization Code with PKCE. The user gives consent, the AS returns a one-time code, the client exchanges it for an RS256-signed JWT bound to the resource (audience). Subsequent /mcp calls carry the JWT. The runner verifies the signature against the AS's JWKS, checks the iss, the aud (RFC 8707 — must match the runner's own public URL), and the expiry. No token passthrough; the runner never forwards the client's token to a downstream API. Why this matters Without audience binding, a token issued for one customer's MCP server could be replayed against another customer's server. RFC 8707 closes that. Without PKCE, a public OAuth client on a desktop is exposed to interception of the authorization code. OAuth 2.1 closes that. ); }