Agentic AI Foundation Logo
The Anatomy of MCP Authorization: How the Hardened Flow Actually Runs

The Anatomy of MCP Authorization: How the Hardened Flow Actually Runs

Ayesha DissanayakaJuly 29, 2026

In a previous blog, I walked the revision timeline from 2024-11-05 to today, dissecting the lessons absorbed by each iteration. But the 2026-07-28 revision is not just another data point; it is the culmination of that entire journey. Shipped just as we go to press, this release represents a decisive security-hardening pass. We have taken hard-won lessons from audience confusion and confused-deputy failures, and codified the fixes into non-negotiable requirements. A timeline explains the evolution, but it cannot show you the machine running against a spec that has fundamentally raised the floor. So let us watch it run, one step at a time.

The setup is deliberately mundane: a desktop assistant is asked by its user, "Pull the Q3 board deck from the company file store." That file store is exposed as a remote MCP server. The assistant holds no token for it and has never spoken to it before. We are going to walk every step from that cold start to the document landing in the agent's hands, stopping each time the story bumps into something the spec insists on.

The cast: the roles it needs

Before the first packet moves, hold roles of OAuth apart cleanly, because everything downstream depends on you not blurring them.

  1. The MCP client is the OAuth client. This is the assistant, the party that will obtain and present a token. In OAuth 2.1 terms, our desktop assistant is a public client acting on behalf of a user. (MCP authorization supports confidential clients too; which one applies depends on the deployment, and public fits this desktop-assistant scenario.)
  2. The MCP server is the OAuth resource server. The file store. It owns the protected resource (your documents) and validates tokens. It does not issue them.
  3. The authorization server issues tokens, and it may be a completely separate entity. It could be the file store's own IdP, or an enterprise IdP the file store trusts. The authorization server handles the grant and issues the token; the MCP server still validates that token, checks its audience, and enforces scopes and its own local policy. Roughly, the AS answers "who is this and what were they granted", and the resource server answers "is this token valid here, and does policy allow this call."

FIG 1 THE THREE ROLES, MAPPED TO OAUTH 2.1

Diagram showing OAuth token flow between MCP Client, MCP Server, and Authorization Server with arrows indicating token presentation and issuance.
USEFUL MENTAL MODEL Essentially, the AS serves as the passport office, whereas the resource server (the MCP server) acts as the bouncer; the AS issues the credentials, but the resource server is the one that validates them against its own local policy and audience restrictions. When people conflate the MCP server with the authorization server, every reasoning error that follows traces back to that one collapse.

The resource owner: the role the other three exist to serve

OAuth 2.1 names four roles, not three, and the fourth is the one the wiring most easily hides: the resource owner, the human user. The person whose calendar, repository, or bank account is at stake. Everything in this interaction happens on their behalf, and their consent is the foundation. If you cannot trace an action back to a grant they gave, you do not have a clever agent; you have an audit problem. This walkthrough uses the common case, a human resource owner with the agent acting on their behalf; MCP authorization also allows flows with no separate human owner, such as an agent acting on its own behalf, so treat the human-owner framing here as this scenario's model, not the only one.

The thesis I want to plant: in the delegated model this walkthrough uses, the client is not the resource owner, and treating them as one is how an agent ends up acting on its own authority instead of someone's behalf. Classic human-facing OAuth blurs the two, because the person clicking "Approve" and the app making the call feel like one actor. Agents break that blur. The desktop assistant fetching the board deck does not own it. The token it carries is not its own power; it is the owner's authority, scoped, time-boxed, and revocable, lent for one narrow purpose.

USEFUL MENTAL MODEL The resource owner is the principal, the agent a fiduciary. A fiduciary that starts making decisions the principal never contemplated has quietly stopped being one. That single line is most of the agentic-authorization threat model.


The resource owner authenticates and consents at the authorization server, which is exactly why "user consent happens here" sits there and nowhere else: the client comes away holding a delegated token, never the owner's identity. But OAuth captures that consent only once, at grant time. The agent then acts autonomously, possibly hundreds of calls later, in ways the person never specifically pictured. Whether that action is still what the owner wanted, consent freshness, is the agent layer's job. The resource owner is the "delegated" and "attributable" properties made concrete: every action traces back to their grant, and every grant is authority on loan, never authority transferred.

Step 1 · The knock: the cold start, "who do I even go to for permission?"

The assistant does the naive thing first. It sends its MCP request to the file store with no Authorization header. This is correct behavior, not a mistake. The agent is not expected to guess where to get a token.

The server answers deterministically with 401 Unauthorized and a WWW-Authenticate header. That header is the hinge of the whole cold start. It does not just say "no." It carries a pointer to the server's Protected Resource Metadata (RFC 9728), the resource_metadata parameter naming a URL the client can fetch.

The client fetches that document and learns, among other things, the authorization_servers this resource trusts. This is the deterministic answer to "who do I go to for permission?" The spec insists on this precisely so the agent never has to infer it, hardcode it, or be socially engineered into using the wrong one. The resource server declares its own trust anchors, in a machine-readable document, at a well-known location.

From the authorization server URL, the client does one more hop: authorization-server metadata discovery (RFC 8414). It fetches the AS's metadata to learn the concrete endpoints (authorization, token, registration if any) and which features the AS supports, such as PKCE methods and issuer identification. The client sends the resource parameter regardless of whether the AS advertises it.

Two fetches, and the agent has gone from knowing nothing to knowing exactly which authorization server governs this resource and how to talk to it. No guesswork, no config file, no human pasting a client secret. That is the property the discovery dance buys you.

FIG 2 COLD START TO AUTHORIZED CALL: THE MACHINE RUNNING

Sequence diagram showing OAuth 2.0 authorization flow between MCP client, server, and auth server.

Steps 1–4 are the discovery dance; 5–7 are the token exchange (the client talks past the resource server directly to the authorization server); 8–9 are the authorized call. The resource server never sees a credential it did not validate for itself.

Step 2 · Identity: identifying the client, with no prior relationship

Now the assistant has to say who it is to the authorization server. In the classic enterprise OAuth world, you would have pre-registered: an administrator creates a client, hands you a client_id, maybe a secret, and both sides remember each other forever. That still works, and the spec supports it. But it assumes a prior relationship, and MCP mostly lives in a world without one. An arbitrary assistant meets an arbitrary file store for the first time. Nobody pre-arranged anything.

Three options, in order of how well they fit that world:

  1. Client ID Metadata Documents (CIMD) are the now-common answer for the open, no-prior-trust case. The client's client_id is a URL, and that URL resolves to a JSON metadata document describing the client. The authorization server dereferences it at request time. No registration call, no shared secret, no out-of-band setup. This is why CIMD matters: it is the mechanism that lets two parties who have never met transact, which is the actual shape of the MCP ecosystem.
  2. Dynamic Client Registration (RFC 7591) is the older programmatic option: the client posts its metadata to a registration endpoint and gets back a client_id. It works, but it makes the AS store state for every ephemeral client that ever knocks, which does not scale gracefully to a long tail of one-off agents.
  3. Pre-registration is the manual case, appropriate when there genuinely is a prior relationship (a first-party assistant against its own vendor's server).
I WANT TO BE PRECISE HERE, BECAUSE THIS GETS MANGLED OFTEN CIMD does not mean the AS trusts anyone who shows up. It means identity is established by reference, meaning the client is identified without prior enrollment rather than authenticated as a specific agent instance. The AS still applies policy to that identity, and CIMD moves the trust decision to request time; it does not remove it.


This is also the first place the agentic-identity thesis shows through the plumbing. Agentic identity is ephemeral, delegated, constrained, and attributable. CIMD mainly buys you identification without a prior relationship: a caller that did not exist yesterday can still present a resolvable identifier the AS can look up and log. Attribution to a user still comes from the grant, the token, and the audit trail, not from CIMD by itself. The delegated and constrained parts come next.

Step 3 · The token request: the load-bearing security step

The assistant now runs the OAuth 2.1 authorization code flow against the endpoints it discovered. Two elements in this request carry almost all of the security weight, and I do not say that loosely.

PKCE with S256 (RFC 7636) is mandatory as of the 2025-11-25 revision. The client generates a random verifier, sends its SHA-256 hash (the challenge) on the authorization request, and reveals the verifier only when redeeming the code. An attacker who intercepts the authorization code cannot use it without the verifier. For public clients, which this desktop assistant is, this is the difference between a stealable code and a useless one. S256 specifically: plain is not acceptable.

The resource parameter (RFC 8707) is the one I will give the most weight to, because it is the direct fix for the failure mode I identified in an earlier blog as the most common production exploit: audience confusion. The client includes resource= naming the specific MCP server it intends the token for. The authorization server binds the resulting token to that audience. The token is now good for this resource server and no other.

Sit with why that matters. Without audience binding, a token minted for the file store is a bearer credential that any other MCP server would also honor if it validates carelessly. A malicious or compromised server could take a token handed to it and replay it against a different resource that trusts the same Identity provider (IdP). The resource parameter closes that by making the token say, in its claims, exactly one place it is allowed to be spent. Audience confusion is not an exotic attack. It is what happens by default when nobody binds the audience. RFC 8707 is how you stop having that default.

2026-07-28 · THE HARDENING HEADLINE This is a change everyone will be talking about, and it is the clearest signal that the authorization work in this release is a security-hardening pass: issuer (iss) validation is now a firm client-side requirement (RFC 9207). The client must confirm the authorization response actually came from the AS it expects, which slams the door on the IdP mix-up attack, the mirror image of audience confusion on the response side. Under 2025-11-25, this was encouraged. As of 2026-07-28, clients MUST validate iss whenever the authorization server includes it.

FIG 3 AUDIENCE BINDING: WHY THE RESOURCE PARAMETER IS LOAD-BEARING

Diagram showing access token routing based on audience validation, with successful match to files.acme.com and failed access to other MCP servers.

The same bearer token, presented to two servers. Binding the audience at issue time is what turns a replayable credential into a single-destination one. Without it, audience confusion is the default, not the exception.

Step 4 · The token in use: Bearer on every request, and the passthrough rule

The assistant redeems the code, gets an access token bound to the file store, and finally retries its original request, now with Authorization: Bearer <token>. The bearer token rides on every request from here. The resource server's job on receiving it is narrow and non-negotiable:

  1. Validate the audience. The token must have been issued for this server. A token whose audience is some other resource gets rejected outright, even if it is otherwise valid and unexpired. This is the enforcement side of the resource parameter; the binding is worthless if the resource server does not check it.
  2. Expect short-lived tokens and revalidate on every call rather than caching a decision. Ephemeral tokens are how you keep a stolen credential from being a durable one.
  3. Never pass the client's token upstream. When the file store needs to call some upstream API of its own (a storage backend, a search service), it must not forward the caller's token. It acts as a fresh OAuth client in its own right and obtains its own credential for that upstream hop.

That third rule is the direct structural defense against the confused-deputy and token-passthrough failures I covered in a previous blog. Token passthrough turns every server in a chain into a replay point for a credential it was merely holding in transit. The confused deputy is what you get when a server uses a caller's authority to do something the caller could not have authorized directly. Refusing passthrough, and re-authenticating at each hop, means authority never leaks sideways through the chain.

FIG 4 NO TOKEN PASSTHROUGH: THE CONFUSED-DEPUTY DEFENSE

Diagram showing MCP Client sending bearer token to MCP Server, which uses fresh credentials to access Upstream API without passing through client credentials.

The caller's authority stops at the resource server. Each edge in the graph is its own authenticated relationship, so a token can never be replayed one hop further than it was issued for.

Step 5 · Least privilege: scopes, the 403, and step-up in motion

The assistant asked to read one board deck. It should not be holding a broad, long-lived grant over the entire file store. This is where scopes and step-up come in, and where the flow starts to look genuinely agentic rather than human-OAuth in a trench coat.

The client requests narrow scope first, say read access to a single collection. If it later attempts something the token does not cover (deleting a file, reading a restricted folder), the server returns 403 with insufficient_scope and, via WWW-Authenticate, names the scope required. The client then performs step-up authorization: a fresh authorization request for the additional scope, subject to whatever consent the AS demands. Permission widens only when a real operation demands it, and each widening is its own auditable event.

MIND THE GAP: THIS IS WHERE THE AGENT LAYER WRAPS Step-up as specified is still human-clicked OAuth stretched over an autonomous caller. The escalation event is where the agent layer has to do its real work, checking that the escalation matches the user’s actual intent and that consent is fresh, not reflexively clicking through because the agent asked. OAuth tells you the grant is valid. It does not tell you the agent should have wanted it.

The floor: authorization responsibilities, distilled

If you are building or integrating with one of these, here is the honest floor, with each item labelled by the role that owns it and how strong the requirement is.

AUTHORIZATION RESPONSIBILITIES BY ROLE - MUST/SHOULD

✓ Resource server (MUST): point clients to its Protected Resource Metadata, either by returning 401 with a WWW-Authenticate header that carries resource_metadata or by serving that metadata at the well-known URI (RFC 9728).

✓ Resource server (MUST): publish Protected Resource Metadata declaring the authorization server(s) it trusts.

✓ Resource server (MUST): validate the token’s audience on every request and reject any token not issued for this server (RFC 8707).

✓ Resource server (MUST): validate each access token before use, accept only tokens issued by an authorization server it trusts, and confirm that the token is intended for this resource server.

✓ Client (MUST): use PKCE with S256 on the authorization code flow (RFC 7636), and validate the issuer (iss) on the authorization response, rejecting the response if the server advertises iss support but omits it (RFC 9207).

✓ Client (MUST): send the resource parameter (RFC 8707) in both the authorization and token requests, naming this server, regardless of whether the authorization server advertises support.

✓ Resource server (SHOULD): on a scope failure, return 403 with insufficient_scope and name the scope required, so the client can step up.

✓ Authorization server (SHOULD): return the iss parameter on authorization responses, and advertise it with authorization_response_iss_parameter_supported (RFC 9207).

That is the machine running. Notice how much of it is plain, well-specified OAuth doing exactly what it was designed to do, and how the MCP-specific parts (discovery, CIMD, audience binding as a default rather than an afterthought) are OAuth composed deliberately for a world of callers with no prior relationship. This is why the MCP authorization spec is the leading edge of agentic IAM standardization, and worth reading even if you never ship a server. It is among the first places the industry is writing down, as normative requirements, what authorization for autonomous callers actually has to look like.

Ayesha Dissanayaka is an Architect at WSO2 focused on identity and access management for AI agents, and an Agentic AI Foundation ambassador.

The Agentic AI Foundation is a neutral, community-governed home for agentic AI standards, hosted by the Linux Foundation. This series is written by an AAIF ambassador.

Share

Author

  • Ayesha Dissanayaka

    Ayesha Dissanayaka

subscription section bg
Subscribe

Subscribe to the AAIF Briefing

Weekly signal on standards, governance, and the people building the future. No fluff. Just what matters.

About AAIF