Back to writing
OAuth · 10 min read

OAuth Was Designed for Humans. AI Agents Change the Rules.

OAuth’s human-centered assumptions start to break when agents act unattended, delegate work, and chain decisions across services.

I will say it plainly. OAuth 2.0, as good as it is, was never built with the idea that the “user” on the other end could be a software agent taking fifty decisions a minute without anybody watching. We have been bending the spec to fit agents, when actually the spec was assuming a human sitting in front of a browser, clicking “Allow.” That assumption is now breaking, and most teams building AI agents have not fully understood how much.

Let me explain what I mean.

The human assumption baked into OAuth

Classic OAuth 2.0 authorization code flow goes something like this. A user clicks login, gets redirected to an identity provider, sees a consent screen, clicks “Allow,” and comes back with an authorization code that the app exchanges for a token. Every single step in this flow assumes a human is present to look at a screen and make a judgment call.

Now put an AI agent in that loop. There is no browser session waiting on the agent’s side. There is no human eyeball reading “This app wants to access your calendar and email.” The agent is not clicking “Allow”; it is calling an API, and it needs to do this at 2 AM, unattended, sometimes chaining five or six calls to different services in a matter of seconds.

The three-legged OAuth dance was built for exactly one thing: a human giving informed consent, in the moment, for a specific access grant. Agents break this in at least three ways.

Where it actually breaks

1. No human present for consent at request time. Consent has to be captured upfront, or delegated in advance, because the agent cannot pause mid-task and wait for someone to click a button. This changes consent from an interactive, moment-of-request thing into a policy that has to be defined beforehand.

2. Identity gets layered, not singular. In classic OAuth, the token represents “this user, authorized this app, for this scope.” With agents, you actually have three identities stacked together: the human who initiated the task, the agent that is executing it, and sometimes a sub-agent that the first agent spawned to do a smaller piece of work. Standard OAuth tokens were not built to carry this chain.

3. Session lifetime assumptions do not hold. A human session might last an hour, a day at most, with natural boundaries like closing the laptop. An agent’s “session” could span a long-running workflow, spin off parallel sub-tasks, and outlive any reasonable human-session pattern, while still needing the same scoped, revocable access a human would get.

Here is the classic flow first, stripped down to its four essential hops:

Classic OAuth 2.0 flow: user signs in, identity provider issues a token, app holds the token, app calls the resource API

One human decision point, right at the start, and one hop from app to resource. Simple.

Now look at what an agent delegation chain actually needs. The human still decides only once, upfront, but everything downstream has to be carried and enforced through tokens instead of another click:

Agent delegation chain: user delegates task, primary agent executes it, identity provider issues a short-lived token, a sub-agent is spawned, resource API receives the scoped token

Same one human decision at the top. But underneath it, two extra hops that a human never sees or approves in real time, and each one has to answer for itself through the token it is carrying, not through a person watching a screen.

What actually helps here

The good news is, we are not starting from zero. RFC 8693, the OAuth 2.0 Token Exchange spec, already gives us a pattern for “on-behalf-of” tokens, where one party can exchange a token for a narrower, downstream token while preserving a chain of who is acting for whom. This maps almost perfectly onto agent delegation, if teams actually use it instead of just minting a fresh static key for every agent.

A rough version of what this looks like in practice:

# Agent requests a narrower, short-lived token to call a downstream API,
# on behalf of the user who originally delegated the task.
 
response = requests.post(
    token_endpoint,
    data={
        "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
        "subject_token": agent_session_token,
        "subject_token_type": "urn:ietf:params:oauth:token-type:access_token",
        "requested_token_type": "urn:ietf:params:oauth:token-type:access_token",
        "scope": "calendar.read",          # narrower than the agent's own scope
        "audience": "calendar-service",    # explicit downstream target
    },
)
 
downstream_token = response.json()["access_token"]
# This token should be short-lived, auditable, and traceable
# back to both the agent and the human who delegated the task.

Combine this with JWT-based claims (RFC 7519) carrying an explicit “acting on behalf of” field, and you get tokens that actually answer the question an auditor will ask you six months later: which agent did this, on whose authority, and with what scope?

There is also real movement happening around authentication patterns specifically for MCP servers, since MCP is quickly becoming the way agents talk to tools and data sources. Worth watching this space closely, because whatever pattern wins there will likely become the default for agent-to-tool auth industry-wide.

The takeaway

OAuth is not broken; it is just incomplete for this new kind of actor. The fix is not to throw it out. It is to extend it: upfront consent instead of interactive consent, token exchange for layered identity instead of one flat token, and short-lived scoped grants instead of a static key living in an environment file for six months.

If your agent architecture still treats the agent as “just another OAuth client” with a long-lived key, you are one incident away from finding out the hard way why the humans-only assumptions in OAuth mattered in the first place.