Most discussions of MCP security start and end with the same question: which server did you install? That question matters, but it is the shallow end. The harder failures live one layer down, in authorization, where an MCP proxy holds credentials on behalf of many clients and can be persuaded to act for the wrong one.
This article is the advanced companion to the MCP setup and review guide. It assumes you already know what a server, a client, and a transport are. The official Security Best Practices document is written for people implementing authorization flows, operating servers, or assessing them, and it is best read alongside the general OAuth security guidance rather than instead of it.
To be clear about what this is: an engineering self-check, not a penetration test. It describes mechanisms and questions to ask about your own deployment. It does not provide payloads or exploitation steps.
The confused deputy, in plain terms
An MCP proxy server connects MCP clients to a third-party API. To that third-party API, the proxy is a single OAuth client. That is the normal, useful design: one integration, many clients.
The trouble starts when the proxy authenticates to the third-party authorization server with a static client ID, meaning the same client identifier for every interaction regardless of which MCP client started it. If the proxy also lets MCP clients register dynamically, each with its own client identifier, the proxy now has two identities in play: its own fixed one with the third party, and a per-client one on the MCP side.
The official document lists the conditions that make the confused deputy attack possible. All of them must be present at once:
- The MCP proxy server uses a static client ID with a third-party authorization server.
- The proxy allows MCP clients to register dynamically, each receiving its own client identifier.
- The third-party authorization server sets a consent cookie after the first authorization.
- The proxy does not implement proper per-client consent before forwarding to the third-party authorization.
That "all at once" is the important part. A deployment that has one or two of these is not automatically exposed, and treating any single condition as a verdict will produce both false alarms and false comfort. The useful exercise is to count how many apply and then look at the combination.
How the chain closes
The official description runs in a fixed order, and the order is what makes the lesson stick.
A user authenticates normally through the proxy to reach the third-party API. During that flow, the third-party authorization server sets a cookie on the user agent indicating consent for the static client ID.
Later, an attacker sends the user a link containing a crafted authorization request. That request carries a malicious redirect URI and a newly registered client identifier, obtained through the dynamic registration the proxy allows.
When the user clicks, the browser still carries the consent cookie from the legitimate request. The authorization server sees the cookie and skips the consent screen. The authorization code is redirected to the attacker's server, as specified in the redirect URI registered during that dynamic registration. The attacker exchanges the code for tokens and now has access as the compromised user.
The hinge is the consent cookie. "Consent" was remembered, and the memory was reused for a request the user never approved. Nothing in the flow looks broken from the user's side; there is no error, no warning, no unexpected screen.
The mitigation follows directly. The proxy must maintain a registry of approved client identifiers per user and check it before starting the third-party authorization flow. Consent has to be per client, not per proxy.
The same logic governs the state parameter. The official guidance requires a cryptographically secure random state per authorization request, stored server-side only after consent is explicitly approved, validated exactly at the callback, rejected when missing or mismatched, single-use, and short-lived. The consent cookie or session holding that state must not be set until the user has actually approved the consent screen. Setting it earlier makes the screen decorative.
Token passthrough and its two failures
Token passthrough is the anti-pattern where an MCP server accepts tokens from an MCP client without validating that they were issued to the MCP server, then passes them through to a downstream API.
The official document separates two dimensions, and conflating them hides the fix.
The first is audience validation failure. If the server does not verify that a token was intended for it, using the audience claim described in the JWT access-token profile, it may accept tokens originally issued for other services. That breaks a fundamental OAuth boundary: a legitimate token for one service becomes usable against another.
The second is the passthrough itself. If the server accepts tokens with the wrong audience and forwards them unmodified, the downstream API may trust the token as though it came from the MCP server, or assume the upstream already validated it. That is the confused deputy again, one hop downstream.
The mitigation is stated as a prohibition, not a preference: MCP servers must not accept tokens that were not explicitly issued for the MCP server. The authorization specification forbids passthrough outright. The same principle runs through the API key hygiene guidance: a credential is scoped to the service that will use it, and forwarding it elsewhere discards that guarantee.
Why the prohibition is mechanical, not stylistic
Two mechanisms explain the rule.
The first is circumvention of security controls. Servers and downstream APIs often implement rate limiting, request validation, and traffic monitoring that depend on the token audience or other credential constraints. When clients can obtain tokens and use them directly against the downstream API, those controls are bypassed, not because anyone disabled them, but because the requests no longer pass through the layer that enforces them.
The second is accountability. A server cannot identify or distinguish clients when they call with an upstream-issued token that may be opaque to it. Downstream logs may show requests attributed to a different identity than the server actually forwarding them. Incident investigation then has to reconstruct who did what from logs that were never designed to answer the question. The logging field discipline is the practical countermeasure: record the client, the forwarding path, and the token's intended audience, not just the endpoint.
There is a quieter cost too. A server that starts as a pure proxy may need to add controls later, and retrofitting audience separation into a system that never had it is much harder than starting with it. The multi-tenant design notes make the same point about identity boundaries generally.
The rest of the map
The official document covers more ground than these two attacks. For each remaining category, the honest summary is its title plus one question to ask about your own deployment. Anything more detailed would be invention on my part.
Server-side request forgery. During OAuth metadata discovery, clients fetch URLs that a malicious server can influence. Ask: are those fetches restricted from private address ranges and routed through a controlled egress path? The same class of attack reaches authorization servers that fetch client metadata: when a server supports Client ID Metadata Documents, it takes a URL from an unknown client and fetches it, so the egress restrictions have to hold there too.
State handle hijacking. MCP has no protocol-level sessions; servers that need state mint an explicit handle and receive it back as a tool argument. Ask: are those handles unguessable and bound to the authenticated user, or can one user's handle be replayed by another?
Local MCP server compromise. Locally running servers execute on the user's machine and may be reachable by other local processes. Ask: are they sandboxed, and is installation gated by explicit consent rather than a startup command hidden in configuration?
OAuth authorization URL validation. Malicious servers can supply dangerous authorization URLs. Ask: does the client reject non-HTTPS schemes and dangerous schemes such as javascript:, and does it avoid shell execution when opening URLs?
stdio transport security in proxy scenarios. The stdio transport is not inherently vulnerable; the risk appears in proxy architectures that spawn servers as child processes. Ask: is the local proxy reachable only by the intended client, and are spawned processes sandboxed and logged?
Mix-up attacks. A client that talks to many authorization servers can be induced to send a code or token issued by one to another. Ask: does the client validate the issuer in the authorization response before redeeming anything?
Localhost redirect URI impersonation. A metadata document can prove control of a domain but not which local process listens on a localhost redirect. Ask: does the authorization server bind the redirect to the registered client tightly enough to reject an impostor process?
CIMD trust policies. Authorization servers that accept URL-based client identifiers can apply domain trust rules. Ask: is there an allowlist, reputation check, or domain-age policy, and is the client hostname shown prominently enough to resist phishing?
Scope minimization. Broad scopes increase the impact of a compromised token and blur audit trails. Ask: does each tool request the narrowest scope it needs, or does the integration ask for wildcard access up front?
That list is a map, not a checklist you can finish. Each item points at a decision that belongs to a specific component: client, proxy, authorization server, or resource server. Assigning the question to the right owner is most of the work.
A self-check you can run this week
- Does your proxy use a static client ID with any third-party authorization server?
- Does it allow MCP clients to register dynamically?
- Does that third-party set a consent cookie after the first authorization?
- Does your proxy implement per-client consent before forwarding?
- Does your server validate the token audience before accepting a request?
- Does it ever forward a client token unmodified to a downstream API?
- Are scopes per tool minimal rather than broad and shared?
- Can your logs distinguish which client called from who forwarded the request?
If all of those conditions describe your deployment, the confused deputy path is open and the fix is per-client consent, not a better cookie. If the answer about forwarding is yes, that is not a risk to weigh; the specification forbids it.
Treat any change here the way you would treat a model change: change one thing, run the flow end to end, and compare behavior against a recorded baseline rather than assuming it still works, as the testing guide describes.
What this is, and is not
None of this means MCP is insecure as a protocol. The failures described here are implementation and architecture problems: a proxy that consolidates identity without tracking it, a server that trusts a token because it arrived, a consent screen that remembers more than it should. Those are ordinary authorization mistakes, and they are fixable with ordinary authorization discipline.
Before changing production configuration, confirm the details against the official document as it stands; the behaviors above were checked on 2026-11-04.