This part walks through the MCP authorization specification, revision 2026-07-28, as an implementer would meet it: what to discover, how to register, how to get a token, and what the server must check. Every requirement here comes from the spec or from the RFCs it cites.
What the spec covers, and what it does not
Authorization is optional in MCP. When an implementation supports it, HTTP-based transports should follow this specification. STDIO servers should not; they take credentials from the environment instead. Other transports must follow the security practices of their own protocol.
The spec makes three roles explicit. The MCP server is an OAuth 2.1 resource server: it validates tokens and never issues them. The MCP client is an OAuth 2.1 client. The authorization server issues tokens, and how it is built is out of scope; it can live with the MCP server or be a separate system, such as your existing IdP.
Three things are deliberately left out, and the rest of the series covers them:
- Which tool calls a valid token may make. The spec ends at “this token is valid for this server”. Part 5 covers decisions per tool call.
- What the server does when it calls other APIs. It must use a separate token for those, which Part 3 covers.
- The general OAuth security baseline. MCP requires the security considerations of OAuth 2.1. The IETF’s broader reference is RFC 9700, Best Current Practice for OAuth 2.0 Security (BCP 240, January 2025), and most of what follows is MCP applying it.
The flow end to end
Here is the whole exchange for a client that starts with only the MCP server’s URL. The sections that follow take each phase in turn.
In practice, discovery runs once per server and its results can be reused. Authorization runs when the client needs a new token. Only the last two steps repeat on every call.
Discovery
A client usually starts with nothing but the server’s URL. Discovery takes it from there to a working authorization server in two steps.
Step 1: find the authorization server. The MCP server must publish OAuth 2.0 Protected Resource Metadata (RFC 9728) with an authorization_servers list of at least one entry. It can advertise the document in two ways, and clients must support both:
- In the
resource_metadataparameter of theWWW-Authenticateheader on a401. If the header carries it, the client must use it. - At a well-known URI, tried in this order: with the MCP endpoint’s path inserted (
https://example.com/.well-known/oauth-protected-resource/public/mcp), then at the root (https://example.com/.well-known/oauth-protected-resource).
If the list has several authorization servers, each is independent. The client chooses one, and must keep separate registrations and tokens per server.
Step 2: read the authorization server’s metadata. MCP uses the standard RFC 8414 suffix and adds OpenID Connect fallbacks. For an issuer with a path, such as https://auth.example.com/tenant1, the client must try in order:
https://auth.example.com/.well-known/oauth-authorization-server/tenant1https://auth.example.com/.well-known/openid-configuration/tenant1https://auth.example.com/tenant1/.well-known/openid-configuration
Without a path, it tries the first two forms at the root. Whatever it fetches, the issuer inside must be identical to the issuer used to build the URL, or the client must not use it. That single string comparison stops a malicious server from passing off another server’s metadata as its own.
Client registration
Part 2 covers the three registration methods in depth. The short version for implementers:
- Order of preference: pre-registered credentials, then a Client ID Metadata Document if the server’s metadata sets
client_id_metadata_document_supported, then Dynamic Client Registration if it has aregistration_endpoint, then asking the user. - Client ID Metadata Documents are the default for clients and servers with no prior relationship. The
client_idis an HTTPS URL with a path, and the document must contain at leastclient_id,client_nameandredirect_uris. - Dynamic Client Registration is deprecated and kept only for older authorization servers. Clients that use it must send an
application_type. - Credentials belong to one authorization server. Pre-registered or DCR credentials are keyed to the server’s
issuer. If Protected Resource Metadata starts pointing elsewhere, the client must re-register and must not reuse them. Metadata-document client IDs are the exception: they work with any server without re-registration.
Authorization servers that accept metadata documents carry most of the risk, so the spec adds duties for them. They should defend against SSRF when fetching, should warn on localhost-only redirect URIs, and must clearly show the redirect URI’s hostname on the consent screen.
Getting the token
The flow is the OAuth 2.1 authorization code grant with three additions that MCP makes mandatory.
PKCE, verified first. Clients must use PKCE with S256 where technically possible. Neither OAuth 2.1 nor PKCE defines how to discover support, so MCP relies on metadata: if code_challenge_methods_supported is missing, the client must refuse to proceed. Authorization servers that use OpenID Connect Discovery must include the field, even though OpenID does not define it.
The resource parameter, always. Clients must send RFC 8707’s resource in both the authorization request and the token request, set to the server’s canonical URI, such as https://mcp.example.com/mcp. They must send it even if the authorization server does not support it. The canonical URI has a scheme, no fragment and, by convention, no trailing slash.
Issuer validation on the response (RFC 9207). Before redirecting the user, the client records the expected issuer alongside the PKCE verifier. When the authorization response comes back, it checks iss before sending the code to any token endpoint:
Server advertises authorization_response_iss_parameter_supported | iss in response | Client action |
|---|---|---|
true | Present | Compare with the recorded issuer |
true | Absent | Reject the response |
false or absent | Present | Compare with the recorded issuer |
false or absent | Absent | Proceed |
The comparison is a plain string comparison with no normalization. On a mismatch, the client must not act on or display any error fields. This is the defense against mix-up attacks, where a malicious authorization server tricks a client into sending it a code issued by an honest one. The spec signals that a future revision will make iss mandatory for authorization servers. Clients should also use and check state.
Using and validating tokens
Sending. The token goes in the Authorization: Bearer header on every HTTP request, never in the query string.
Validating. The server must validate every token under OAuth 2.1 before processing the request. In particular, it must check the token was issued for this server as its audience, and reject any token that does not name it. Invalid or expired tokens get a 401.
Never passing tokens through. This is the rule MCP states most firmly. A server must not accept or transit any token other than its own. When it calls an upstream API, it acts as an OAuth client to that API with a separate token from the upstream authorization server. It must never forward the token it received. Clients, in turn, must not send a server any token except one issued by that server’s authorization server.
Storing. Clients and servers must store tokens securely: tokens in client storage, or cached or logged on the server, are the usual way they get stolen. Authorization servers should issue short-lived access tokens and must rotate refresh tokens for public clients. RFC 9700 adds the alternative of sender-constraining them with DPoP or mutual TLS.
Refresh tokens. Clients that want them must keep them confidential, and must not assume they will be issued. They may request offline_access if the server lists it. MCP servers should not ask for offline_access in their challenges or metadata: a refresh token is the client’s concern, not the resource’s.
Proxy servers. An MCP server that fronts a third-party API with a static client ID is exposed to the confused-deputy problem: an attacker with a stolen authorization code can get tokens without the user’s consent. Such servers must obtain user consent for each dynamically registered client before forwarding to the third-party authorization server.
Scope challenges and step-up
Scopes are requested incrementally. The server’s 401 challenge should carry a scope parameter with what the request needs. If it doesn’t, the client requests all of scopes_supported from Protected Resource Metadata, which is meant to be the minimum for basic use.
When a token turns out to lack scope at runtime, the server answers:
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope",
scope="files:write",
resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
The rules on each side:
- Server: put every scope the operation needs into one challenge, not one at a time across retries. The required scopes may depend on the request’s arguments. The server must also honour scope hierarchies: a broader scope that implies a narrower one is enough.
- Client: request the union of the scopes it already asked for and the new ones, so earlier permissions are not lost. Retry a few times at most, then treat it as a permanent failure. Clients acting for a user should attempt step-up; clients acting for themselves (
client_credentials) may simply abort.
This handles “the token needs more scope”. It does not handle “this particular call needs a human to approve it”, which is Part 6.
Requirements by role
The main requirements in one place. Each is a MUST unless the cell says should:
| Requirement | MCP client | MCP server | Authorization server |
|---|---|---|---|
Protected Resource Metadata (RFC 9728) with authorization_servers | Must use it for discovery | Must serve it | |
| AS metadata (RFC 8414 or OIDC Discovery) | Must try all well-known forms; must check issuer | Must provide at least one | |
| PKCE | Must use, S256 when capable; must refuse if unsupported | Must support OAuth 2.1; OIDC servers must list code_challenge_methods_supported | |
resource parameter (RFC 8707) | Must send in authorization and token requests | ||
| Issuer check on the response (RFC 9207) | Must validate iss as in the table above | Should send iss | |
| Audience validation | Must reject tokens not issued for it | ||
| Token passthrough | Must send only tokens from the server’s own AS | Must not accept or forward other tokens | |
| HTTPS | Redirect URIs must be HTTPS or localhost | All endpoints must use HTTPS | |
| Redirect URIs | Must register them | Must match them exactly | |
| Refresh tokens | Must rotate for public clients | ||
| Credentials per AS | Must keep separate state per AS; must re-register when the AS changes |
Sources
- MCP Authorization, revision 2026-07-28
- MCP Authorization Server Discovery
- MCP Client Registration
- MCP Authorization Security Considerations
- The OAuth 2.1 Authorization Framework, draft-ietf-oauth-v2-1
- RFC 9700: Best Current Practice for OAuth 2.0 Security
- RFC 9728: OAuth 2.0 Protected Resource Metadata
- RFC 8414: OAuth 2.0 Authorization Server Metadata
- RFC 8707: Resource Indicators for OAuth 2.0
- RFC 9207: OAuth 2.0 Authorization Server Issuer Identification
- RFC 6750: Bearer Token Usage