MCP authorization: how OAuth 2.1 works in the 2026 spec
This post explains how MCP authorization works. If you are here because a VerifyMCP score flagged your endpoint and you want the code that fixes it, go straight to add OAuth (RFC 9728) auth to your MCP server.
MCP authorization is OAuth 2.1 with the MCP server cast as a resource server. The client obtains an access token from an authorization server, attaches it as a bearer token to every HTTP request, and the MCP server validates that the token was issued for it specifically before doing anything. The current spec revision, 2026-07-28, makes authorization optional for MCP implementations: HTTP-based transports SHOULD conform to the OAuth profile, while stdio servers SHOULD NOT use it at all and take credentials from the environment instead.
That one paragraph hides a lot of moving parts, and the 2026-07-28 revision moved several of them. Most guides you will find were written against the 2025-06-18 revision and still describe dynamic client registration as the recommended mechanism; the current spec deprecates it. This post walks through what the spec actually requires today, standard by standard, with each claim cited to the spec or the RFC it builds on. We run the scoring engine that probes every remote server in the official MCP registry for exactly these behaviours, so we will also show you how much of this is deployed in practice.
Which standards make up MCP authorization?
The spec deliberately implements a subset of existing OAuth standards rather than inventing anything new. These are the pieces and what the spec requires of each:
| Standard | Role in MCP | Requirement |
|---|---|---|
| OAuth 2.1 (draft 13) | The authorization framework itself, with PKCE built in | Authorization servers MUST implement it |
| RFC 9728 Protected Resource Metadata | Tells clients where the server’s authorization server lives | MCP servers MUST implement; clients MUST use it for discovery |
| RFC 8414 / OpenID Connect Discovery | Publishes the authorization server’s endpoints and capabilities | Server MUST offer at least one; clients MUST support both |
| RFC 8707 Resource Indicators | Binds each token to one specific MCP server | Clients MUST send resource; servers MUST validate audience |
| Client ID Metadata Documents | An HTTPS URL acts as the client ID | Clients and authorization servers SHOULD support |
| RFC 7591 Dynamic Client Registration | Programmatic client registration | MAY support; deprecated as of 2026-07-28 |
| RFC 6750 Bearer Token Usage | How the token travels: Authorization header only | Tokens MUST NOT appear in query strings |
How does authorization work in MCP?
Three roles, straight out of OAuth 2.1. The MCP server is a resource server: it accepts requests carrying access tokens and validates them. The MCP client is an OAuth client acting on behalf of a user. The authorization server authenticates the user and issues tokens, and it can be run by the same operator as the MCP server or be a separate identity provider. The spec deliberately leaves the authorization server’s internals out of scope, which is what lets an MCP server sit behind Okta, Entra, Auth0 or a self-hosted issuer without protocol changes.
That split also answers the “MCP authentication vs authorization” question. The MCP server never sees a username or password. Authentication (proving who the user is) happens at the authorization server; what the MCP server enforces is authorization: whether this token grants this operation. When people search for “MCP authentication”, the mechanism they find is this OAuth flow, because it is the only user-identity mechanism the spec defines for HTTP transports.
Two hard rules govern the token itself. It travels only in the Authorization: Bearer header, on every single request, and it MUST NOT be placed in a URI query string. And it must be validated on the server, not merely present: invalid or expired tokens get an HTTP 401, insufficient scope gets a 403.
What is protected-resource metadata for?
Protected-resource metadata (RFC 9728) is how a client discovers, with no out-of-band configuration, where to get a token for a given MCP server. It is a small JSON document the MCP server MUST publish, either at a well-known path (/.well-known/oauth-protected-resource, optionally with the server’s path inserted after it) or via a URL advertised in its 401 response. The document names the server’s canonical resource identifier and, in a field the spec requires, at least one entry in authorization_servers.
This is the piece that makes remote MCP servers plug-and-play. Without it, a bare 401 tells a client that it needs a token but not where to obtain one, and every client would need per-server configuration. With it, the client can go from “I have a URL someone pasted into a config file” to a completed OAuth flow with no human in the loop except the consent screen. The authorization server discovery rules then take over: the client fetches the authorization server’s own metadata (trying the RFC 8414 path first, then OpenID Connect Discovery) and MUST reject any metadata document whose issuer does not exactly match the URL it was derived from, which blocks a hostile host from impersonating someone else’s issuer.
What does a client actually do on a 401?
A conforming client treats the 401 as the start of the flow rather than a failure. The full sequence it runs:
- Send the request without a token. The server replies
401 Unauthorizedwith aWWW-Authenticate: Bearerheader that SHOULD carry aresource_metadataURL and may carry thescopeneeded. - Fetch the protected-resource metadata from that URL, or fall back to the well-known paths (path-inserted first, then root) if the header did not provide one. Clients MUST support both routes.
- Pick an authorization server from
authorization_serversand fetch its metadata, validating the issuer as above. - Obtain a client ID, by preference order: pre-registered credentials, then a Client ID Metadata Document, then dynamic client registration as a fallback.
- Run the OAuth 2.1 authorization code flow in the user’s browser, with PKCE (mandatory in OAuth 2.1) and the
resourceparameter naming the MCP server. The client records the expected issuer and, when anissvalue comes back in the authorization response, MUST check it matches before exchanging the code. - Exchange the code for an access token (sending the PKCE verifier and the
resourceparameter again) and retry the original MCP request withAuthorization: Bearer <token>.
A related flow handles the 403. If a valid token lacks scope for a particular operation, the server SHOULD reply 403 Forbidden with error="insufficient_scope" and the scopes the operation needs; the client is expected to re-authorize with the union of its previous scopes and the new ones, a pattern the spec calls step-up authorization. This is why well-behaved MCP servers request minimal scopes up front rather than everything on day one.
Is dynamic client registration required?
No, and as of the 2026-07-28 revision it is deprecated. The spec’s client registration section says clients and authorization servers MAY support RFC 7591 dynamic client registration, retained only “for backwards compatibility with authorization servers that do not support Client ID Metadata Documents”. This is a real reversal. Earlier revisions relied on dynamic registration to solve the cold-start problem of a client meeting a server it has never seen before, and that reliance drew pointed criticism from enterprise identity teams, since few corporate identity providers will expose an unauthenticated registration endpoint that mints stored credentials for every connecting client.
The replacement is the Client ID Metadata Document: the client’s ID is simply an HTTPS URL pointing at a JSON document the client hosts, containing its client_id, client_name and redirect_uris. The authorization server fetches and validates that document on demand, checking that the client_id inside matches the URL exactly and that any redirect URI in the authorization request appears in the document. No registration call, no stored state, and the same client ID works across every authorization server that supports the mechanism, where dynamically-registered credentials are bound to the single issuer that minted them and MUST NOT be reused elsewhere.
The practical reading for server operators: if you run your own authorization server, advertising client_id_metadata_document_supported: true in your metadata is the current-spec path. Keeping an RFC 7591 endpoint alive is legitimate for older clients, but it is now the compatibility option, not the recommendation. Our note on what changed when this revision shipped covers how this moved scores.
How do resource indicators stop token replay across servers?
An agent talks to many MCP servers, so the failure mode that matters is a token issued for one server being accepted, or quietly reused, by another. RFC 8707 resource indicators close this from both ends:
- The client MUST include a
resourceparameter, set to the target MCP server’s canonical URI, in both the authorization request and the token request, whether or not the authorization server supports it. The authorization server audience-restricts the issued token to that resource (via the JWTaudclaim or equivalent). - The MCP server MUST validate that each token it receives was issued specifically for it as the intended audience, and MUST reject anything else with a 401.
The blast radius this contains is the confused-deputy scenario. Without audience binding, a malicious or compromised MCP server that receives your token can replay it against every other server your authorization server covers, and a server sitting in front of a downstream API can pass your token along and act with your full authority rather than its own narrower grant. The spec is blunt about the second case: MCP servers “MUST NOT accept or transit any other tokens”, and clients MUST NOT send a server any token that its own authorization server did not issue. Token passthrough is banned outright, not discouraged.
How does session management work now MCP is stateless?
There is no protocol session to manage any more. The 2026-07-28 revision removed the initialize handshake and the Mcp-Session-Id header from the Streamable HTTP transport: every request now stands alone, carrying its protocol version and capabilities in _meta, and a server that needs continuity mints an explicit handle and passes it back as an ordinary tool argument. Full details of the revision are on our 2026-07-28 spec page.
For authorization this is a simplification with teeth. Identity comes from the bearer token on the request, every request, and from nowhere else. There is no session object that could be hijacked to inherit someone’s authenticated state, and no temptation to authenticate once at handshake time and trust the connection afterwards, because there is no handshake and no connection state. Statelessness is also why remote MCP servers can now run on serverless infrastructure without sticky routing: the token is the only context the server needs.
How many MCP servers actually require authentication?
Declared enforcement remains rare. As of 18 August 2026, 1,487 of 12,682 remote components in the official registry declared a required secret header in their registry entry. That is a count of what servers say about themselves, not of what we observed: a server can demand OAuth without declaring any header, so the genuinely authenticated population is larger than this figure. Read it as a floor.
That number needs careful reading, because an open endpoint is not automatically a failure. A server exposing public, read-only data is a legitimate open service, and the spec itself makes authorization optional. The cases that matter are servers exposing private data, writes, or destructive tools with no authorization check at all; our threat model walks through why an unauthenticated destructive tool is the state we treat as an outright failure. It is also fine to keep tools/list public and require a token only to call a tool: enforcement at the call site is enforcement where it counts.
If you maintain a remote MCP server and want to add spec-conformant authorization, the step-by-step guide is in our docs: add OAuth (RFC 9728) auth to your MCP server covers the metadata document, the 401 challenge, and exactly what our probe looks for on the next crawl. To see how any given server behaves today, look it up in the registry: the authorization signal on its score page records what an anonymous caller could actually reach. Those scores are independent estimates rather than guarantees, and where a server was too locked down for us to observe a signal we mark it inconclusive rather than guessing either way.