OAuth in MCP: Threading User Identity Through Tool Servers
The first time you wire an MCP server into a real production system, you discover something the tutorials gloss over: the protocol gives the agent capabilities, but it does not give the tool server an answer to the question every audit log requires — which human is this acting on behalf of? You can ship a working demo without resolving that question. You cannot ship to a regulated enterprise without resolving it. And the gap between those two states is almost entirely a distributed-systems problem dressed up as an OAuth problem.
What teams reach for in that gap, in roughly the order they reach for it, is a tour of every anti-pattern the OAuth working group has spent fifteen years warning against. A shared service account in the MCP server's environment. A long-lived per-user token pasted into a config. A cheerful "we'll just forward the user's session cookie and let the downstream service figure it out." Each one works in staging. Each one breaks in a different way the first time security review actually looks at it.
The MCP authorization specification, finalized through 2026, leans hard on OAuth 2.1 with PKCE, RFC 8707 resource indicators, RFC 7591 dynamic client registration, and RFC 9728 protected resource metadata. That stack tells you how the agent acquires a token for the MCP server. It deliberately does not tell you how the MCP server then calls the five other systems it needs to call to actually do its job. That second hop — agent → MCP server → downstream API — is where identity threading lives, and it is where most production deployments are still improvising.
The three failure patterns you will see in the wild
The shared service account is the most common, because it is the easiest. The MCP server holds a single GitHub PAT, a single Jira API key, a single internal service token, and every tool call goes out under that identity. The system works. Then someone deletes a production resource through the agent, the audit log shows [email protected] as the actor, and incident response cannot determine which human triggered it. Worse, every per-user authorization decision the downstream service would normally make — can Alice see this PR? can Bob delete this issue? — is now a decision the MCP server has to re-implement, badly, against a permissions model it does not own. This is the confused deputy problem with extra steps, and it is the exact pattern OWASP's MCP Top 10 calls out as MCP01: token mismanagement and secret exposure.
The long-lived per-user token is the second move. Each user generates a personal access token, the MCP server stores them keyed by user ID, and every tool call uses the right one. The audit trail is correct, but you have just built a credential vault with none of the rotation, revocation, or scope-narrowing properties a real one has. The tokens have no expiry your security team would accept, often grant far broader scopes than the agent needs, and live in whatever store the MCP server happens to use — frequently a Postgres column, occasionally an environment variable, sometimes a file on disk. The first phishing incident makes this very obvious very quickly.
The session-cookie passthrough is the third, and it is the one that fails most surprisingly. The reasoning sounds clean: the user is already logged in, the agent runs in their browser context, just forward the session and let the downstream service see the same authenticated user it always sees. This works exactly as long as the agent and every tool server live inside the same SSO domain. The moment a tool server lives in a different cookie scope — a different subdomain configuration, a different OIDC provider, a partner SaaS — the cookie either does not arrive or arrives stripped of the cross-site flags that would make it valid. The MCP specification explicitly forbids token passthrough for related reasons: a token issued for audience A should never be presented to audience B, because the audience claim is the entire mechanism that prevents one service from being tricked into acting as another.
What the protocol actually solved, and what it left for you
The 2026 MCP authorization specification is a real improvement, and it is worth being precise about what it covers. The MCP server is modeled as an OAuth 2.1 resource server. The MCP client is an OAuth 2.1 client. PKCE is mandatory. Dynamic client registration via RFC 7591 means a Claude Desktop or a Cursor can show up at an MCP server it has never seen before, register itself, and complete an auth code flow without any out-of-band coordination. Resource indicators via RFC 8707 require the client to name the specific MCP server it intends to call, which lets the authorization server scope the resulting token's audience claim narrowly enough that it cannot be silently replayed against a different server. Protected resource metadata via RFC 9728 lets the MCP server publish where its authorization server lives, so clients can discover the auth flow rather than hardcoding it.
That is a complete and credible answer to how does the agent get a token for the MCP server. It is not an answer to how does the MCP server make calls to other services on behalf of the user. The specification is clear that token passthrough is forbidden — the MCP server cannot just forward whatever token the agent presented — but it leaves the actual delegation pattern up to the implementer. There is an open issue on the MCP repository, #214, asking for explicit on-behalf-of token exchange support precisely because this seam is where every enterprise integration is currently improvising.
- https://modelcontextprotocol.io/specification/draft/basic/authorization
- https://dasroot.net/posts/2026/04/mcp-authorization-specification-oauth-2-1-resource-indicators/
- https://stackoverflow.blog/2026/01/21/is-that-allowed-authentication-and-authorization-in-model-context-protocol/
- https://datatracker.ietf.org/doc/html/rfc8693
- https://www.rfc-editor.org/rfc/rfc8707.html
- https://github.com/modelcontextprotocol/modelcontextprotocol/issues/214
- https://auth0.com/ai/docs/mcp/get-started/call-your-apis-on-users-behalf
- https://www.solo.io/blog/mcp-authorization-patterns-for-upstream-api-calls
- https://bishopfox.com/blog/otto-support-confused-deputy
- https://bishopfox.com/blog/otto-support-ssrf-token-passthrough-with-mcp
- https://owasp.org/www-project-mcp-top-10/2025/MCP01-2025-Token-Mismanagement-and-Secret-Exposure
- https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-on-behalf-of-flow
- https://www.scalekit.com/blog/enterprise-mcp-how-identity-sso-and-scoped-auth-actually-work
- https://www.aembit.io/blog/mcp-oauth-2-1-pkce-and-the-future-of-ai-authorization/
- https://www.osohq.com/learn/authorization-for-ai-agents-mcp-oauth-21
