Appendix B: OAuth 2.0 and 2.1 Quick Reference
This page follows the MCP authorization specification in revision 2026-07-28. Chapter 19 builds each step below on the tickets server, in the same order.
Grant Type Comparison
| Grant type | Best fit | Notes |
|---|---|---|
| Authorization Code + PKCE | User-facing clients | The default for public clients |
| Client Credentials | Service-to-service calls | No end-user delegation; defined in MCP’s Client Credentials extension, not core |
| Device Code | Limited-input devices and some CLI flows | Useful when browser-based sign-in is awkward |
Discovery, in Order
Each step, and the URLs a client tries (→ ch19 §The First Request Usually Fails):
- The client calls the MCP server without a token and gets
401 Unauthorized. The server should includeresource_metadatainWWW-Authenticate, and ideally ascopeparameter too. - The client fetches protected resource metadata from the header URL. If the header has no URL, it tries the path-suffixed well-known URI and then the root one.
- The client checks that the metadata’s
resourcevalue matches the MCP server’s resource identifier (RFC 9728 §3.3). It then picks an authorization server from theauthorization_serverslist. - The client fetches authorization server metadata (RFC 8414), falling back to OpenID Connect Discovery, which MCP clients must also support. For an issuer with no path (
https://auth.example.com), it tries/.well-known/oauth-authorization-serverand then/.well-known/openid-configuration. For an issuer with a path (https://auth.example.com/tenant1), it tries three URLs in order:/.well-known/oauth-authorization-server/tenant1,/.well-known/openid-configuration/tenant1and/tenant1/.well-known/openid-configuration. - The client rejects any metadata document whose
issueris not identical to the issuer it used to build the URL (RFC 8414 §3.3). - The client registers or identifies itself (see below), then runs Authorization Code + PKCE.
What the Bytes Look Like
These are the tickets server’s documents from Chapter 19; compare your curl -i output field by field.
Step 1 is the 401 for a request that carried no token. Leave out error when no token was sent (RFC 6750 §3.1). Add error="invalid_token" when a token was sent and rejected.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp", scope="tickets:read"
Content-Type: application/json
Step 2 is GET https://mcp.example.com/.well-known/oauth-protected-resource/mcp:
{
"resource": "https://mcp.example.com/mcp",
"authorization_servers": ["https://auth.example.com"],
"scopes_supported": ["tickets:read", "tickets:write", "deployments:read"],
"bearer_methods_supported": ["header"]
}
Step 3 compares resource with the configured URL byte for byte; a trailing slash or a different host fails (Chapter 22’s 401 loop).
Step-up is a 403 sent when the token is valid but a tool needs more. Here delete_deployment needs a scope the token lacks:
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope", scope="deployments:write", resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp"
Authorization Code + PKCE in One Screen
- The client creates
code_verifierandcode_challenge, and records the authorization server’sissueralongside them. - The user is redirected to the authorization server. The request includes
resource(RFC 8707), set to the MCP server’s canonical URI. - The user authenticates and grants consent.
- The client receives an authorization code. If the response carries
iss(RFC 9207), the client compares it with the recorded issuer by simple string comparison before doing anything else. If the server advertisesauthorization_response_iss_parameter_supportedandissis missing, the client rejects the response. - The client exchanges the code,
code_verifierandresourcefor tokens. - The client calls the protected MCP server with the access token in the
Authorizationheader (Bearerscheme) on every request, never in the query string.
Client Credentials in One Screen
- A confidential client authenticates to the authorization server.
- The authorization server issues an access token.
- The client calls the protected resource as itself.
Client Registration Options
Before the flow starts, an MCP client needs a client ID. In 2026-07-28, a client that supports every option should try them in this order (→ ch19 §Registration; as a tree in Appendix L):
- Pre-registered credentials for that authorization server, if the client has them.
- Client ID Metadata Documents (CIMD), used when the authorization server advertises
client_id_metadata_document_supported. The client ID is an HTTPS URL that serves a JSON document containingclient_id,client_nameandredirect_uris. The authorization server fetches the document and checks it. - Dynamic Client Registration (RFC 7591), when the server advertises a
registration_endpoint. Deprecated; a client using it must sendapplication_type(nativefor desktop, CLI and localhost apps). - Asking the user to enter client details.
Stored credentials are bound to their issuer; never reuse them elsewhere. CIMD client IDs are portable.
Error Handling Cheatsheet
| Status | Typical meaning | What the client should do |
|---|---|---|
401 Unauthorized | Missing or invalid token | Re-authenticate or refresh |
403 Forbidden | Token valid but insufficient (error="insufficient_scope") | Step up: re-authorize with the union of previously requested scopes and the challenged scopes, with a retry limit |
Send resource_metadata on both 401 and 403, and one scope listing everything the operation needs. An ID token is not an access token.
Scopes Follow Annotations
Name scopes after capabilities, not roles (tickets:read, not admin_everything), and let a tool’s annotation decide which scope it needs (→ ch19 §A valid token is not a permission):
| Scope | Tools it unlocks | Annotation on those tools | Requested |
|---|---|---|---|
tickets:read | search_tickets | readOnlyHint: true | Up front |
tickets:write | tools that change a ticket | readOnlyHint: false | Up front |
deployments:write | delete_deployment | destructiveHint: true | By step-up only |
Minimal Security Checklist
- PKCE enabled for public clients
- token expiry tested
- refresh behavior tested where applicable
- token validation performed server-side
- scopes mapped to real policy
- protected resource metadata published
issvalidated when present in authorization responsesresourceparameter sent in authorization and token requests; tokens checked for audience server-side
Key RFCs and Standards
- RFC 6750: Bearer Token Usage. Used in discovery step 1 (the
WWW-Authenticatechallenge) - RFC 9728: Protected Resource Metadata. Used in discovery steps 2–3
- RFC 8414: Authorization Server Metadata. Used in discovery steps 4–5
- OpenID Connect Discovery 1.0. Used in discovery step 4
- OAuth Client ID Metadata Document (IETF draft). Used in registration option 2, the preferred mechanism
- RFC 7591: Dynamic Client Registration. Used in registration option 3 (deprecated in MCP 2026-07-28)
- RFC 7636: PKCE. Used in Authorization Code step 1
- RFC 8707: Resource Indicators. Used in Authorization Code steps 2 and 5
- RFC 9207: Authorization Server Issuer Identification. Used in Authorization Code step 4
- RFC 6749: OAuth 2.0. Used in Authorization Code steps 5–6
- OAuth 2.1 (IETF draft): the baseline the MCP authorization spec builds on