MCP Token Passthrough: How an Agent Hands Over Its Access

MCP Token Passthrough: How an Agent Hands Over Its Access

Written by

in

An AI agent rarely works alone. It calls out to tools and servers using the Model Context Protocol, MCP, and those calls carry an access token that proves who the user is. MCP token passthrough is the failure where an MCP server takes a token that was never meant for it and either replays that token to other APIs or hands it onward. The MCP authorization spec names this an explicitly forbidden anti pattern, and for good reason. One careless server can spend the user’s access anywhere the token is accepted.

What MCP token passthrough actually is

Start with how OAuth is supposed to work. When a token is issued, it carries an audience claim, written aud, that says which service is allowed to accept it. A token minted for the Google Calendar API has aud set to that API. The whole point is that the token is a key cut for one specific lock. A correct server checks that claim on every request and rejects anything not cut for its own lock.

Token passthrough breaks that rule in two ways. Either the MCP server accepts a token whose audience is not itself, or it takes a token it received and forwards it to an upstream API. Both moves treat the token as a general purpose pass instead of a scoped key. The audience boundary is the one promise OAuth makes, and passthrough throws it away.

A token is a key cut for one lock. The moment a server uses someone else’s key on a different door, the entire scoping model is gone.

The confused deputy hiding inside it

This is a fresh coat of paint on an old problem. A deputy is a program that holds authority and acts for others. It becomes confused when it is tricked into using its authority on behalf of the wrong party. An MCP server that passes tokens through is exactly that. It sits between the agent and the wider world, holding tokens that flow through it, and a malicious or compromised server can collect those tokens and reuse them against services it was never supposed to touch. We wrote about the general shape of this in confused deputy problems in AI agents, and token passthrough is one of the cleanest examples of it.

The agent trusts the server. The downstream API trusts the token. Nobody checks that the server is the party the token was meant for. That gap is where the abuse lives.

Why this matters more for agents

A human clicks one button at a time. An agent fans out across many tools in a loop, often with little review of each call. If the servers it talks to are untrusted or quietly swapped, as in a rug pull attack, a single passthrough server can harvest a stream of tokens at machine speed. The same blast radius shows up when a server lies about its tools, which we cover in tool poisoning.

A concrete example: the Acme Calendar server

Imagine an MCP server called Acme Calendar. The user has connected it so their agent can read and create events. The agent already holds a Google style access token for the user’s calendar.

In the broken design, the agent simply ships that Google token to Acme Calendar, and Acme uses it directly. Here is the bad flow.

1. Agent  -> Acme Calendar:  Authorization: Bearer <google_token, aud=googleapis>
2. Acme   -> Google API:      Authorization: Bearer <google_token>   (replayed as is)
3. Acme   -> some other API:  Authorization: Bearer <google_token>   (why not, it works)

Acme never checks the audience. It just forwards a token cut for Google to wherever it likes. If Acme is malicious, or if anyone has compromised it, that token is now logged, stored, and replayable. The user thought they granted calendar access. They actually handed a working key to a stranger who can keep using it until it expires.

Now the correct design. Acme Calendar is registered as its own resource with its own audience. The agent obtains a token scoped to Acme, and Acme verifies the audience before doing anything.

1. Agent  -> Acme Calendar:  Authorization: Bearer <acme_token, aud=acme_calendar>
2. Acme:   verify token.aud == "acme_calendar"  -> ok, this token is for me
3. Acme   -> Google API:      Authorization: Bearer <acme_own_token>  (its own credential)

The difference is the whole game. In the safe flow the token Acme receives is one it is allowed to hold, and when Acme needs to call Google it uses its own separate credential that the user consented to. No key meant for one door is ever tried on another.

How to stop token passthrough

The defenses are concrete and they stack. None of them is hard to apply once you treat the audience claim as a hard boundary rather than a suggestion.

  • Validate the audience on every request. Before an MCP server does any work, it checks that aud matches itself. If the token was minted for a different service, reject it with a 401. No exceptions, no fallback.
  • Never forward a received token upstream. A token that arrived at your server stays at your server. When you call a downstream API, you use your own credential obtained through your own consented flow, not the caller’s key.
  • Use the proper OAuth flow per resource. The agent should get a distinct token for each resource it talks to, each with the right audience. Treat every MCP server and every downstream API as its own resource with its own scope.
  • Keep tokens short lived and narrowly scoped. A token that expires in minutes and grants one action is far less useful to a thief than a long lived token that can do anything. Small scope and short life shrink the damage of any leak.
  • Log and alert on audience mismatches. A rejected token with the wrong audience is a signal, not noise. Count those rejections and alert when they spike, because a mismatch often means a misconfigured client or someone probing for a passthrough hole.

A quick test you can run

Take a token issued for service A and send it straight to your MCP server. A correct server rejects it because the audience does not match. A server with a passthrough bug accepts it and, worse, may turn around and use it. If that token works where it should not, you have found the flaw before an attacker did.

The assumption that breaks

Every safe token system rests on one quiet assumption: that whoever holds a token is the party it was issued for. MCP token passthrough is what happens when a server stops checking that and starts treating tokens as cash that spends anywhere. The audience claim is right there in the token, ready to be verified, and the entire failure is the decision to ignore it. This is the kind of bug you find by asking what a server takes for granted about the tokens it receives, not by scanning for a known bad string. That is exactly what an autonomous researcher built to test assumptions is meant to do. Read more on our about page.

Frequently asked questions

What is MCP token passthrough?

It is when an MCP server accepts an access token that was not issued for it and either replays that token to downstream APIs or forwards it onward, instead of checking that the token’s audience matches itself. The MCP authorization spec names this an explicitly forbidden anti pattern.

Why is token passthrough dangerous?

It breaks the audience boundary of OAuth, so a token meant for service A can be replayed to service B, which defeats the point of scoped tokens. It also turns the server into a confused deputy that acts with someone else’s authority, letting a malicious or compromised server collect and reuse tokens it should never see.

How does token passthrough relate to the confused deputy problem?

A confused deputy is a program that holds authority and is tricked into using it for the wrong party. An MCP server that passes tokens through sits between the agent and other services holding tokens that flow through it, so it can be made to spend the user’s access on doors the token was never cut for.

How do you prevent MCP token passthrough?

Validate the token audience on every request and reject any token not minted for this server, never forward a received token to an upstream API, use the proper OAuth flow so the agent gets a token scoped to each resource, keep tokens short lived and narrowly scoped, and log and alert on audience mismatches.

How can I test whether my MCP server is vulnerable?

Take a token issued for a different service and send it to your MCP server. A correct server rejects it because the audience does not match. If the server accepts the token, or worse reuses it downstream, you have found a passthrough flaw before an attacker does.