Sitemap

Tangled Tokens and Authorized Agents

6 min readMay 15, 2025

--

Right now, many folks are excited about the prospect of agentic AI: intelligent computer systems that can access your stuff and do useful things for you, all without you having to program them to do it. But in order for that to happen, these bots need a way to actually go and do things. The Model Context Protocol (MCP) was recently proposed as a common interface for agents to get access to services through a proxy.

The MCP Proxy Pattern

The idea is pretty solid at its core: an MCP server provides a common API for agents to query services and data, and for services to advertise what actions are available to the agents. The MCP server sits in the middle to facilitate the whole shebang. For this to work, we need two distinct authorization contexts connected by the MCP server. Thankfully, OAuth gives us a great set of tools to address this, and Aaron Parecki wrote a fantastic piece about how OAuth can be applied to help solve this problem, I consider that article required reading for anyone in this space.

As it turns out, though, the MCP deployment pattern defies some of the assumptions about how OAuth ought to work.

Two Worlds

The proxy setup splits the world into an MCP Protocol space, where the agent connects to an MCP server, and what we’ll call the upstream service space, where the MCP server connects to some other service that does the actual work. This is where OAuth starts to come into play.

The OAuth flavored parts of the MCP Server

If we look at MCP as an OAuth-protected API, we can pretty easily see how we can split out the AS and RS roles inside the MCP server space. In the general case, it’s easy to see how the AS portion can facilitate the user authorizing the agent. The agent gets an OAuth token to call the MCP server, which maps to some set of credentials upstream. We don’t want to just pass through the MCP client’s token, though — there’s no guarantee the upstream service even uses OAuth, let alone the same set of credentials. So while we will need a mapping between these sides, this pattern allows us tons of flexibility in how we roll this out.

In the enterprise case, we can use existing AS policy to authenticate users to the MCP server and map their agent’s access to whatever sets of services that user can use. The integration between the service and the MCP server can be handled by the enterprise, without users needing to do any extra work. We can even allowlist this connection so that users don’t have to see a consent screen, as long as all the right policy conditions are in play. If the integrated service uses OAuth itself, we could even apply token exchange between the MCP client’s access token and the service’s required access token, to limit exposure. The enterprise can even lock down the MCP server to use only pre-registered, pre-approved MCP clients, already a common pattern among corporate OAuth deployments.

On the other end of the deployment spectrum, we might have an agent, MCP server, and upstream service all deployed and owned by completely separate entities with no prior relationship. This might sound crazy, but it’s not really any different from how desktop email clients work with the IMAP protocol. For most IMAP use cases, the mail client stores the user’s credentials and impersonates them on the API. With OAuth, we can probably do better than that, but OAuth was built to connect websites together in a world where things are more predictable and stable.

So how can we do this in a wildly dynamic MCP space?

Intertwined Credentials

OAuth classically requires the user to log in to the AS and approve the client, which is registered with the AS, to act on their behalf to call a resource. While we can apply that pattern to the MCP proxy, and as we saw in the enterprise case it can make a lot of sense, I propose that we can learn more from the world of email clients.

An email server isn’t going to know anything about a particular instance of email software ahead of time, and the server probably isn’t even going to know anything about a class of email software. A user could, if they chose, implement IMAP from scratch and use it with the server — that’s the promise of interoperability for protocols like IMAP. The server only cares if the protocol is implemented correctly and if the mail client can authenticate to the user account. If the authentication works, then the client is valid.

I argue that it’s much the same in the MCP proxy case. The identity of a particular instance of client software is less important because it should always be mapped to a particular set of access rights upstream. And where does the MCP server get those access rights? From the user authorizing the MCP server somehow. This could be yet another OAuth flow, it could be storing the user’s credentials, or it could be something very un-OAuth like accessing a local socket connection. If the MCP server can make a valid connection to the upstream service in the context of the user setting up the connect to their agent, then that’s all an average MCP server should really care about. The token that it issues to the agent will get mapped to the authenticated context used to call the upstream service. When the MCP server’s AS issues a token for the agent, the AS can store a mapping to the authentication needed for the upstream service. This is not dissimilar from mapping an OAuth access token to the user who was logged in to the AS at the time, and making that information available to the RS. In this case, our RS also needs to make a client call to something else, and that’s the information we make available.

I could be true that we might not even need client IDs in the traditional OAuth sense. The only security artifact that matters is the access token and its mapping to the upstream credential set. If I need to re-connect my upstream API, my agent can get a new token as a result and just use that. The MCP server might not even care about who I am, so long as I can successfully connect to the upstream system. After all, this is exactly the argument used for the design of OAuth clients in general: if they can get to the resources they need, they don’t need to know who the user is in many cases. (And if they do, there’s OIDC for that.)

Weaving Webs

This work is bringing to light some of the limitations and assumptions of the OAuth protocol. Some of these are solved by things that we built into GNAP, especially the notion of ephemeral clients, but even with GNAP it’s not a simple world.

For example, if the upstream service requires a static OAuth registration and then allowlists a client after a user authorizes it, does this leave open a door for attackers to exploit? Is there anything that the MCP server, sitting in the middle as a proxy, needs to do to help this? What about cases where the MCP server has no user interface at all?

There are many questions still left to be answered, but I, for one, am excited that they’re being asked and discussed right now. I look forward to being part of the conversation, and I hope you can join in. Maybe we’ll even invite the bots to help.

--

--

Justin Richer
Justin Richer

Written by Justin Richer

Justin Richer is a security architect and freelance consultant living in the Boston area. To get in touch, contact his company: https://bspk.io/

Responses (1)