What you will be able to do
- Decide whether an MCP connection should use OAuth or environment credentials based on its transport
- Trace how an MCP client discovers the authorization server and registers itself, and spot a deprecated registration path
- Check a token flow for the binding, transport and PKCE requirements whose absence leaves a security gap
Key concept
Audience-bound access token — An access token is only valid for the MCP server it was issued for. The client names that server when it requests the token, and the server rejects any token that was not issued for it. Most of the gaps on these pages come from breaking one side of that binding.
1.Start with the transport: OAuth or environment credentials
Before you review scopes or tokens, find out how the client reaches the MCP server. The MCP authorization specification is written for HTTP. A server reached over HTTP should follow the OAuth-based flow in the spec. A local server started over STDIO should not: the spec tells it to take credentials from its environment instead. Any other transport has to follow the established security practices for that protocol.
This tells you which requirements apply. If a design document asks for a full OAuth handshake to a local STDIO subprocess, it is asking for the wrong thing. If a remote HTTP server takes requests with no authorization flow at all, ask why. The spec says HTTP implementations SHOULD conform, so this is a departure from a recommendation rather than a breach of a MUST, but it still needs a justification in the review. For HTTP the baseline is OAuth 2.1, and the spec says authorization servers "MUST implement OAuth 2.1 with appropriate security measures for both confidential and public clients."
| Transport | What the MCP authorization spec says |
|---|---|
| HTTP-based | SHOULD conform to the authorization specification (OAuth 2.1 and the referenced RFCs) |
| STDIO | SHOULD NOT follow the specification; retrieve credentials from the environment |
| Alternative transports | MUST follow established security best practices for their protocol |
Checkpoint 1 of 5· Check yourself
A team runs a local MCP server as a STDIO subprocess and plans to make it run the full OAuth 2.1 authorization flow. What does the MCP specification say?
The authorization spec targets HTTP-based transports. For STDIO it tells implementations to take credentials from the environment instead.
“Implementations using an STDIO transport SHOULD NOT follow this specification, and instead retrieve credentials from the environment.”Source: modelcontextprotocol.io
Sources1
2.Discovery and client registration
On an HTTP connection, the client first has to find out which authorization server protects the MCP server. The spec gives that job to OAuth 2.0 Protected Resource Metadata (RFC9728), and the requirement applies to both sides: the server must publish the metadata, and the client must use it for discovery. When the server rejects an unauthenticated request with 401, it includes the metadata URL in the WWW-Authenticate header under resource_metadata. The metadata can also be served at a well-known URI. The client then reads the authorization server's own metadata, using either OAuth 2.0 Authorization Server Metadata (RFC8414) or OpenID Connect Discovery. The server needs to offer only one of these, but the client must support both.
When you review a design, check that both sides do their part. A server with no protected-resource metadata, or a client that hardcodes an authorization server instead of discovering it, does not meet these requirements.
Next, the client has to identify itself to the authorization server. Two spec versions matter here, and they should be kept apart. The 2025-11-25 spec gives the priority order: use pre-registered client information if the client has it; otherwise use Client ID Metadata Documents if the authorization server advertises client_id_metadata_document_supported; if neither applies, fall back to Dynamic Client Registration when the server exposes a registration_endpoint; as a last resort, prompt the user for client details. The later 2026-07-28 spec keeps Client ID Metadata Documents as a SHOULD and adds that Dynamic Client Registration is deprecated, retained only for backwards compatibility with authorization servers that do not support metadata documents. This lesson treats 2026-07-28 as the current spec: where the two differ, it is the one to assume, and the practical result is the same ordering with DCR flagged as a legacy path. A design that relies on DCR with a server that supports metadata documents uses that legacy path.
Metadata documents bring their own checks. The client_id must be an HTTPS URL with a path, and the document must include client_id, client_name and redirect_uris. The fetched document's client_id must match the URL exactly. Redirect URIs presented in an authorization request must be validated against those in the metadata document. A design that omits either check does not meet these requirements.
Checkpoint 2 of 5· Put it in order
Put the client registration options in the priority order an MCP client should try them
- 1.Use Dynamic Client Registration as a fallback if supported
- 2.Use pre-registered client information if available
- 3.Use Client ID Metadata Documents if the authorization server advertises support
- 4.Prompt the user to enter client information
The spec ranks pre-registration first, metadata documents second, Dynamic Client Registration as a fallback, and manual entry last.
“Use Dynamic Client Registration as a fallback if the Authorization Server supports it”Source: modelcontextprotocol.io
3.Binding, sending and protecting the token
Getting a token is half the job. The other half is making sure it can only be used where it was meant to be used. Clients must include the resource parameter in both the authorization request and the token request. That parameter must identify the MCP server the token is for, using the server's canonical URI (for example https://mcp.example.com/mcp). A URI with no scheme or with a fragment is not valid. The server, in turn, must check that every token it receives was issued for it. If either step is skipped, a token issued for one server can be replayed against another.
The spec also fixes how the token travels. It goes in the Authorization header as a Bearer token and never in the URL.
GET /mcp HTTP/1.1
Host: mcp.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...The security considerations add more requirements a reviewer can check:
- HTTPS everywhere. All authorization server endpoints must use HTTPS, and redirect URIs must be either localhost or HTTPS.
- PKCE or stop. If the authorization server metadata lacks code_challenge_methods_supported, the server does not support PKCE and the client must refuse to continue. The same applies to OpenID Connect discovery: if that field is missing, the client refuses.
- Issuer check. If the server advertises authorization_response_iss_parameter_supported as true and the response has no iss, the client rejects the response. When iss is present, the client compares it with the issuer it recorded.
- Refresh tokens. Clients must keep refresh tokens confidential and must not assume one will be issued.
Checkpoint 3 of 5· Check yourself
An authorization server's RFC8414 metadata has no code_challenge_methods_supported field. What must the MCP client do?
The spec treats a missing code_challenge_methods_supported as no PKCE support, and the client must stop rather than continue without it.
“If code_challenge_methods_supported is absent, the authorization server does not support PKCE and MCP clients MUST refuse to proceed.”Source: modelcontextprotocol.io
Audience binding fails in two steps, and a gap review should look for both. The 2025-06-18 spec describes them. First, an audience validation failure: an MCP server that does not verify that tokens were intended for it may accept tokens originally issued for other services, which lets attackers reuse legitimate tokens across services. Second, token passthrough: if the server not only accepts tokens with the wrong audience but also forwards them unmodified to downstream services, it can cause the "confused deputy" problem. The downstream API may trust the token as if it came from the MCP server, or assume the token was already validated. When you review a server design, ask two questions: does it check the audience of every incoming token, and does it ever pass a client's token on to an upstream API?
Checkpoint 4 of 5· Check yourself
An MCP server accepts a token that was not issued for it and forwards it unchanged to a downstream API. Which problem does the spec say this can cause?
Accepting a wrong-audience token and forwarding it unmodified is token passthrough, which can cause the confused deputy problem because the downstream API may trust the token as if the MCP server had validated it.
“forwards these unmodified tokens to downstream services, it can potentially cause the “confused deputy” problem”Source: modelcontextprotocol.io
Checkpoint 5 of 5· Exam question
While auditing an Agent SDK integration, you find allowed_tools=["Read", "Glob", "Grep", "Bash"] combined with permission_mode="bypassPermissions", and a canUseTool callback that logs every request and denies anything touching a credentials directory. The developer believed this callback was their safety net. What should the audit conclude?
Correct answer: A — The callback is shadowed: calls approved earlier in the order, including everything bypassPermissions approves, never reach canUseTool; a PreToolUse hook is needed if every call must be checked
- A. Correct. The SDK evaluates hooks, then deny rules, then ask rules, then permission mode, then allow rules, and only then calls canUseTool. With bypassPermissions set, the mode step approves every call that reaches it, so Bash and everything else never falls through to the callback. A logic check that must run on every call belongs in a PreToolUse hook instead, since hooks run first and a hook deny applies even in bypassPermissions mode.
- B. bypassPermissions is not scoped only to unlisted tools; it approves every call that reaches the permission-mode step, listed or not, so Bash calls are approved before the callback is ever consulted.
- C. This reverses the evaluation order. Hooks, deny rules, ask rules, and permission mode are all checked before allow rules and canUseTool, so canUseTool is one of the last steps, not the first.
- D. The requiresUserInteraction exception applies to AskUserQuestion and specially annotated MCP tools, but it does not change the outcome for the ordinary built-in tools in this scenario, which are fully shadowed by bypassPermissions regardless of that flag.
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.Passing the access token as a URL query parameter is fine as long as the connection uses HTTPS.Why is that wrong?
The spec requires the Authorization: Bearer header and bans tokens in the query string, whatever the transport security.
Covered in Binding, sending and protecting the token
2.Dynamic Client Registration is the standard way for a new MCP client to register.Why is that wrong?
In the current spec, Dynamic Client Registration is deprecated and kept for backwards compatibility. Client ID Metadata Documents are preferred.
Covered in Discovery and client registration
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“Implementations using an STDIO transport SHOULD NOT follow this specification, and instead retrieve credentials from the environment.”
↩︎ Start with the transport: OAuth or environment credentials“Implementations using an HTTP-based transport SHOULD conform to this specification.”
↩︎ Start with the transport: OAuth or environment credentials“MCP servers MUST implement OAuth 2.0 Protected Resource Metadata (RFC9728). MCP clients MUST use OAuth 2.0 Protected Resource Metadata for authorization server discovery.”
↩︎ Discovery and client registration“Note that Dynamic Client Registration is deprecated and retained for backwards compatibility with authorization servers that do not support Client ID Metadata Documents.”
↩︎ Discovery and client registration“MUST identify the MCP server that the client intends to use the token with.”
↩︎ Binding, sending and protecting the token“MUST NOT assume refresh tokens will be issued; the AS retains discretion”
↩︎ Binding, sending and protecting the token“Access tokens MUST NOT be included in the URI query string”
↩︎ Exam trap 1“Note that Dynamic Client Registration is deprecated and retained for backwards compatibility with authorization servers that do not support Client ID Metadata Documents.”
↩︎ Exam trap 2 - 2.
“MUST validate redirect URIs presented in an authorization request against those in the metadata document”
↩︎ Discovery and client registration“Client ID Metadata Documents: When client and server have no prior relationship (most common)”
↩︎ Prediction“Use Dynamic Client Registration as a fallback if the Authorization Server supports it”
↩︎ Checkpoint - 3.https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization/security-considerationsOfficial docs
“All redirect URIs MUST be either localhost or use HTTPS.”
↩︎ Binding, sending and protecting the token“MCP servers MUST validate that tokens presented to them were specifically issued for their use”
↩︎ Key concept“If code_challenge_methods_supported is absent, the authorization server does not support PKCE and MCP clients MUST refuse to proceed.”
↩︎ Checkpoint - 4.
“forwards these unmodified tokens to downstream services, it can potentially cause the “confused deputy” problem”
↩︎ Binding, sending and protecting the token