Skip to content

The act Claim Nobody Checks: OAuth Token Exchange and the A2A Delegation Chain

8 min read Fullmakt Team

  • authentication
  • credentials
  • oauth
  • a2a
  • traceability
  • data-ownership
  • governance
  • agents

OAuth already has an answer to “who is this agent acting for, three hops into a chain” — it’s called Token Exchange, RFC 8693, and it shipped in 2020, years before anyone was wiring agents to call other agents. It defines a grant type built specifically for one service exchanging a token for a new one on behalf of another party, and a claim — act — meant to carry the chain of actors forward, hop by hop. We’ve written before about the identity question agentic AI’s login handshake skips. RFC 8693 is the piece that was supposed to answer it. Almost nobody running A2A delegation today actually uses it — and the deployments that do use it mostly don’t verify the one claim that makes it worth using.

What Token Exchange actually specifies

A standard OAuth grant answers one question: is the bearer of this token allowed to do X. Token Exchange adds a second dimension. A client holding a subject_token (the thing being acted on) can present an actor_token (who is doing the acting) to a token endpoint and request grant_type=urn:ietf:params:oauth:grant-type:token-exchange. The token that comes back carries an act claim — a nested object naming the actor, which can itself nest a previous act claim from an earlier hop. Walk the act chain on any token and you get the full sequence of parties that have acted on a subject’s behalf, in order, back to the original principal.

That’s precisely the shape an A2A delegation chain needs: Agent A, invoked by a user, asks Agent B to update a record; B asks Agent C to call a downstream API. Token Exchange is the standards-track mechanism for minting C’s token so it says “C, acting for B, acting for A, acting for the user” — not just “C, authenticated as C,” which is what a plain client-credentials grant at each hop produces.

Where it breaks down in practice

Having the mechanism available isn’t the same as it being load-bearing. Three gaps show up repeatedly:

  • Most A2A implementations skip Token Exchange entirely. It’s easier to give each agent its own client credential and let it authenticate to the next hop as itself. That satisfies the A2A handshake — the receiving agent gets a valid token — but the chain of “who asked for this” that Token Exchange was built to preserve never gets minted in the first place. This is the flattened-identity pattern from our on-behalf-of post, and Token Exchange existing in the spec doesn’t help a deployment that never calls it.
  • When it is used, the act claim is trusted, not verified. A resource server that receives a token with an act chain three levels deep usually checks the signature on the outer token and stops. Nothing forces it to confirm that each nested actor was actually authorized to act for the one above it, or that the chain wasn’t assembled by a token endpoint that will mint an act claim for anyone who asks. An unverified act claim reads like provenance. It’s closer to a self-reported field.
  • The chain answers “who,” not “under what authority.” RFC 8693 names the actors. It says nothing about whether Agent B was allowed to delegate to Agent C for this specific task, whether C’s scope should be narrower than B’s, or what happens if C tries to act after the user revoked the original grant. Identity and authorization are different questions, and a valid act chain only ever answers the first one.

The net effect: teams that adopt Token Exchange often get a false sense that delegation is now traceable, because the token looks like it encodes the chain. Whether that chain is true, current, and authorized at every hop is a separate enforcement problem the spec deliberately leaves to the deployment.

This isn’t the same gap as contextId

We’ve written separately about why contextId and taskId aren’t trace IDs — that’s a transport-layer correlation problem: following one logical task across hops for observability. Token Exchange operates one layer down, at the authorization decision itself: who was allowed to make this specific call, under whose delegated authority. A system can have perfect trace correlation and a completely unverified act chain, or vice versa. Both have to hold for an incident review to answer “who did this, and were they allowed to.”

What actually has to be true

  • A token’s act chain has to be minted by something that checks authorization at every hop, not accepted as an unverified claim already present on an incoming token. If Agent C’s token claims it’s acting for B acting for A, that chain needs to trace back to real, checkable delegation grants — not a token endpoint that will nest any actor a caller supplies.
  • Delegated scope has to narrow, not just pass through. A Token Exchange response should be capable of granting C less than B had, for exactly this task — the same least-privilege discipline that matters for a single agent’s credentials matters again at every hop of a chain.
  • Revocation has to propagate down the chain, not just at the root. If the user revokes the grant that started the chain, every act-chained token downstream needs to stop working — not remain valid until its own expiry because nothing is checking the root grant’s current status.
  • The act chain and the audit trail need to be the same record, not two systems that happen to agree today. A tamper-evident audit log that can’t reconstruct the same delegation chain a token’s act claim asserts is a log that can be quietly wrong.

The business case: the broker mints the chain, not the agent

Fullmakt’s decision-token and credential-dispensing model already separates “is this call allowed” from “here’s the credential” into two brokered, logged steps. Delegation chains fit the same shape, and the fix is the same one behind every gap this series has described: don’t let the claim of authority originate anywhere the broker didn’t mint it.

  • Every hop’s delegated authority is issued by the broker’s policy engine, not asserted by the calling agent. When Agent B asks the broker to authorize a downstream call to Agent C on the user’s behalf, the broker checks the policy for that delegation before issuing anything — the same gate that already governs a single agent’s scoped credentials applies to a second and third hop.
  • The chain the broker issues is the chain in the audit trail, by construction. Because the same system mints the delegated authorization and writes the log row, there’s no separate “verify the act claim matches reality” step — the record is the authorization, tied to the same task and identity as every other brokered call that agent makes.
  • Revocation is a single check, not a propagation problem. Because downstream authority is issued per call against a live policy decision rather than baked into a long-lived token, revoking the root grant takes effect on the very next hop’s request — there’s no stale act chain still validating against an OAuth server that hasn’t heard the user changed their mind.
  • Scope narrows by policy, at every hop, automatically. A delegation request that would hand Agent C more than Agent B is itself permitted to delegate gets refused before a token is minted — the same least-privilege ratchet applied to chains instead of single grants.

FAQ

Do we need to implement RFC 8693 ourselves to get chain traceability? Not if a broker sits at every delegation point already deciding whether a call is allowed and dispensing what’s needed for it. The broker can issue delegation authority in whatever form the deployment needs — RFC 8693’s act claim is one standard shape for it, and a useful one to expose to downstream resource servers, but the enforcement has to happen at issuance, not at a resource server hoping the claim it receives is true.

Is an unverified act claim worse than not having one? In one respect, yes — it creates false confidence. A missing act claim tells a reviewer “we don’t track this,” which at least prompts questions. An act claim nobody verifies looks like an answer and can be wrong, which is a harder failure to catch during an incident review.

Where does this fit next to A2A’s own authentication? A2A’s handshake proves which agent is calling. Token Exchange, used correctly, proves which chain of principals that agent is calling for. Both are necessary; neither substitutes for the other, and neither substitutes for a policy decision on whether the delegation should have been allowed in the first place.

Does this apply to MCP too, or only A2A? The chain problem is sharpest in A2A because agent-to-agent delegation is the common case, but any MCP deployment where one server calls back into another service on the user’s behalf has the identical gap — a token that authenticates the server, not the delegation.

What’s the minimum viable fix if we can’t adopt Token Exchange this quarter? Route every downstream delegation through a policy check that names the full chain of actors before anything is authorized, and log that decision alongside the call it authorized. The wire format of the resulting token matters less than whether the authorization behind it was actually checked.

RFC 8693 gave OAuth the right shape for a delegation chain years before A2A needed one. The shape isn’t the guarantee. An act claim is only as trustworthy as whatever minted it — which is exactly why that minting belongs at a broker that checks policy on every hop, not inside a token an agent three calls deep is simply asking the next service to believe.