The Confused Deputy and Agent Identity
The confused deputy Hardy defined in 1988 reappears intact in MCP proxies and agent delegation. This post walks through the capability answer, RFC 8693 token exchange, and dedicated agent identities that record who acted on whose behalf.
A program holding authority from two sources can be tricked whenever it has no way to express which one it is acting under, and an agent sits in exactly that position.
A deputy holding authority from two sources
The confused deputy is a concept Norm Hardy defined in 1988. The paper appeared in ACM SIGOPS Operating Systems Review, volume 22, issue 4. The original definition describes a program that runs while holding authority from two sources and has no means of distinguishing them. Such a program can be tricked into misusing one authority while acting for another master.
The paper is titled "The Confused Deputy (or why capabilities might have been invented)". The concept was written up to justify capabilities as the alternative.
The case was a FORTRAN compiler on the Tymshare system. The compiler ran while holding a home files license that let it reach files in its own home directory. When a user named the billing file (SYSX)BILL as the debug output file, the compiler overwrote it under that strong license and the billing information was lost.
Authority came from two sources here. One was the authority the caller delegated by running the compiler with RUN (SYSX)FORT. The other was the compiler's own license. The compiler had no way to state which authority it intended to open the file with, so the operating system applied the stronger one.
The root cause is ambient authority. When a request does not carry the source of its authority, the system automatically applies the full set of privileges the running principal holds. A file name designates only what to open, not whose authority opens it.
How a capability binds designation to authorization
A capability is a reference that designates a target and grants authorization to it at the same time. Because designation and authorization are fused into one, the separate step of taking a name and then checking permissions disappears. Had the compiler opened its output file only through a capability received from the caller, it would have failed outright, since it held no capability for the billing file.
Access control based on an access control list (ACL) does not stop this attack. An ACL only checks whether the requesting principal holds permission on the resource, and the deputy genuinely holds that permission.
| Aspect | ACL | Capability |
|---|---|---|
| What the request carries | The target name | A reference fusing target and authority |
| Basis for the decision | All privileges of the running principal | The capability carried in the request |
| Confused deputy | Not prevented | Prevented |
The stopgap Hardy proposed at the time was a system call called switch hats. It made the deputy choose explicitly, on every call, whether to act under its own authority or the caller's, and today's on-behalf-of delegation is the ancestor of that idea.
Hardy himself judged the approach insufficient. Real applications needed more than one authority at once, and a model that picks one of two values did not generalize to that situation. The limits of expressing delegation with only a subject and an actor were therefore already pointed out in 1988.
The path revived in MCP proxies
The agent stack reproduces this structure without modification. A Model Context Protocol (MCP) server or proxy sits where it receives a user token and calls a downstream API under its own credentials. It holds authority from two sources, and if it does not record which one it acts under on each request, it stands where the 1988 compiler stood.
The MCP security guidance calls the first variant token passthrough and forbids it as an anti-pattern. The behavior is forwarding a received token straight to a downstream API without verifying that the token was issued to this server. Controls the server placed on the path are bypassed, the subject in the audit record blurs, and the same token gets reused at other services.
| Claim | Incoming token | What the MCP server should expect |
|---|---|---|
| aud | https://storage-api.example | https://mcp-proxy.example |
| scope | files.read.all | files.read.project |
If the proxy forwards without inspecting aud and actor, the downstream storage API accepts the request as a normal call from the proxy. The clue that catches this flow in logs is the same jti and the same token hash appearing on both sides of the proxy.
The second variant is the OAuth confused deputy. It holds when the proxy acts toward a third-party API under a single static client ID and the four conditions below are met at once.
- A static client ID is used at the third-party authorization server
- Dynamic registration of MCP clients is allowed
- The third party sets a consent cookie after the first authorization
- The proxy does not enforce per-client consent before forwarding
A consent cookie is the trace of permission a user granted to one client, and the boundary collapses when the proxy reuses it for every client. User consent must stay scoped to the client that requested it, and an authorization code must travel only to a registered callback. In both variants, who is acting on whose behalf toward which resource disappears.
How RFC 8693 expresses delegation
RFC 8693 OAuth 2.0 Token Exchange became an IETF Proposed Standard in January 2020. The specification defines delegation and impersonation as separate things. In delegation, actor A keeps its own identity while acting on behalf of B. In impersonation, A receives all of B's rights and becomes indistinguishable from B.
RFC 8693 supports both semantics. An on-behalf-of label does not make a flow a delegation by itself. Some implementations, such as Microsoft on-behalf-of (OBO), can be closer to impersonation, so delegation has to be chosen explicitly.
A delegation chain is expressed with two parameters and one claim. subject_token carries the party being acted for, and actor_token carries the actual actor. Nesting the act claim in the issued JWT puts the current actor at the outermost level and the previous actor inside, so tool calls and sub-agent re-delegation remain as a chain.
Nested prior actors are informational only when an access decision is made. The consumer of the token decides using the top-level subject and the current actor alone. The whole chain is an audit lineage, while the authorization judgment ends at the current hop.
RFC 8693 explicitly supports service chains and delegation hops. A resource server exchanges a token it received for a new token used on a backend call, taking the client role itself. That maps directly onto an agent stack where authority descends into sub-tools and sub-agents, which makes it the closest existing standard to agent delegation.
Downscoping, narrowing the issued token by scope, resource, and audience at every exchange, is the axis of the defense. Microsoft OBO propagates delegated scopes only and pins application roles to the user principal instead of passing them to the app. The aud of the assertion token must also match the app requesting OBO, and a token issued for a different resource is refused for exchange. Restricting the audience is what blocks token reuse.
Advance control over re-delegation comes from the may_act claim. It authorizes ahead of time which party is eligible to become an actor on behalf of another party, at the moment the token is issued. This is the mechanism that draws a policy boundary around which agents and tools may act for the user.
Microsoft OBO does not use the token-exchange grant of RFC 8693. It is a separate mechanism that puts a requested_token_use=on_behalf_of parameter on the RFC 7523 jwt-bearer grant. The names look alike, but it is distinct from the IETF standard token exchange, so interoperability cannot be assumed.
Dedicated agent identities
Even with delegation formalized, the chain is empty if there is no identity to put in the actor slot. Giving an agent a human account or a shared service account leaves only the human in the audit record and erases the principal that actually acted. Non-human identity (NHI) is the layer that fills the slot.
Microsoft gives four reasons for keeping agent identities separate.
- Agent work is attributed separately from staff, customers, and workloads. Every query is recorded as performed by a specific agent.
- The lifecycle is large in scale and short-lived, with thousands created and destroyed per day.
- IAM protections such as adaptive access, risk detection, and lifecycle management apply at the same level as for human accounts.
- Each agent has at least one human sponsor, and when a sponsor leaves the organization the agent transfers to their manager automatically.
The standard for workload identity is SPIFFE, a CNCF graduated project. A SPIFFE ID follows the RFC 3986 URI form spiffe://<trust-domain>/<path>. The scheme must be spiffe, the trust domain must not be empty, query and fragment are forbidden, and the total length stays at or below 2048 bytes.
An SVID is that SPIFFE ID plus a signature and an optional public key, issued through a simple Workload API. It replaces hardcoded credentials with short-lived machine identities and supports mutual authentication over mTLS or JWT. It is valid only when signed by the authority of its trust domain, and SPIRE is the reference implementation.
Microsoft Entra Agent ID approaches the same problem from the IAM product side. It is a dedicated identity distinct from a service principal. A service principal assumes long-term stability, while an agent identity is designed for scale and short lifetimes. The implementation is an agent subtype on top of service principal infrastructure. It consists of four objects: blueprint, blueprint principal, agent identity, and agent user.
Permissions start at the minimum. They begin with delegated scopes inherited from the blueprint and widen through an access package when needed. An agent cannot open the interactive /authorize screen itself, so the client obtains a user token and hands it over through an OBO exchange. As of July 2026, however, the feature is in preview and gated behind premium licensing.
The protocol layer still lags. Microsoft announced A2A support and stated that enterprise-grade identity support for A2A and MCP is being designed together with the industry. That statement itself shows that identity and authentication at this layer are pre-standardization. For now, gateways and authorization servers outside the protocol have to fill the gap.
Operational checklist
Catching a confused deputy requires recording the values around a token rather than the token itself. The actor, the audience, the scope, and the consent lineage are the real security data.
Logs to keep
- Token hash and the
iss,sub,actor,aud,scope,tenant,exp,jticlaims - The
client_idof the originating request, the redirect URI, and the consent record - Parent-child lineage of token exchanges and downstream call receipts
Detection signals
- Audience mismatch, or a missing or changed actor
- Scope growing across a delegation hop
- The same token used by more than one client
- A previously unseen redirect URI, or an authorization code issued with no consent record
Enforcement points
- Accept only tokens whose audience is the MCP server itself
- Bind the token to its target resource with RFC 8707 Resource Indicators
- Token exchange and downscoping instead of forwarding
- Per-client consent, exact redirect URI matching, short TTLs, and proof-of-possession
Whether a control has been breached is judged by three comparisons. If a transaction succeeds while matching any one of the items below, the boundary has failed.
- The effective actor differs from the approved actor
- The resource called differs from the approved resource
- The effective scope is not a subset of the approved scope
Summary
A confused deputy arises when a program holding authority from two sources cannot express which one it is acting under. Hardy closed that gap in 1988 with capabilities, which fuse designation and authorization, and the agent stack reopens the same gap in MCP proxies. Forwarding a token downstream without validation, or reusing a consent cookie across every client, leaks the user's authority as it stands.
RFC 8693 is the closest existing standard for expressing delegation at the protocol level. Its instruments are subject_token and actor_token, nested act claims, downscoping, and may_act. Identity is the remaining half. Give agents an identity separate from people through SPIFFE or Entra Agent ID, and keep the actor, the audience, and the consent lineage in the logs.