Delegation chain explorer
An orchestrator delegates to a sub-agent with token exchange
Alex asks an orchestrator agent for a report. The orchestrator holds a token for the documents API in Alex's name and hands part of the work to a research sub-agent. Each hop exchanges the token it holds for a narrower one with an act claim, so the API can see the whole chain of who acted for whom, and no hop can do more than the one before it.
MisconfigurationScope accumulation
The authorization server grants the sub-agent's exchange request more scope than the subject token carried: the sub-agent asked for docs:read docs:admin and got it, although the token it exchanged only had docs:read. Authority grew at a hop that should only ever narrow it.
The chain, step by step
Step 1: Alex to Orchestrator agent
The orchestrator already holds an access token for the documents API that Alex obtained through the usual authorization code flow.
1.1. Delegation vs. Impersonation SemanticsOAuth 2.0 Token Exchange rfc
Alex's access token held by the orchestratortyp at+jwt · alg ES256 · kid docs-2026
- iss
- https://auth.docs.example
- sub
- alex
- aud
- https://api.docs.example
- client_id
- orchestrator
- scope
- docs:read docs:write
- exp
- 1790000000
- iat
- 1789996400
- jti
- a1
Unchanged since the previous token in this chain.
Step 2: Orchestrator agent to auth.docs.example
Delegation, not impersonation: the orchestrator identifies itself with an actor token and asks for less than it holds.
2.1. RequestOAuth 2.0 Token Exchange rfc
Step 3: auth.docs.example to Orchestrator agent
4.1. act (Actor) ClaimOAuth 2.0 Token Exchange rfc
Delegated token for the orchestratortyp at+jwt · alg ES256 · kid docs-2026
- iss
- https://auth.docs.example
- sub
- alex
- aud
- https://api.docs.example
- client_id
- orchestrator
- scopechanged
- docs:read
- actadded
{ "sub": "orchestrator" }- may_actadded
{ "sub": "research-subagent" }- expchanged
- 1789998200
- iatchanged
- 1789996500
- jtichanged
- a2
6 claims differ from the previous token in this chain. may_act says which party the subject allows to act on this token next; the authorization server checks it on the following exchange.
Step 4: Orchestrator agent to Research sub-agent
The sub-agent does not present this token to the API. It is the subject token for the sub-agent's own exchange.
1.1. Delegation vs. Impersonation SemanticsOAuth 2.0 Token Exchange rfc
Delegated token, handed to the sub-agenttyp at+jwt · alg ES256 · kid docs-2026
- iss
- https://auth.docs.example
- sub
- alex
- aud
- https://api.docs.example
- client_id
- orchestrator
- scope
- docs:read
- act
{ "sub": "orchestrator" }- may_act
{ "sub": "research-subagent" }- exp
- 1789998200
- iat
- 1789996500
- jti
- a2
Unchanged since the previous token in this chain.
Step 5: Research sub-agent to auth.docs.examplereplaces "second-exchange"
2.1. RequestOAuth 2.0 Token Exchange rfc
Step 6: auth.docs.example to itselfreplaces "may-act-check"
The specification leaves the issued token's scope to the authorization server's policy. A policy that lets a delegated token exceed its subject token breaks the chain's one guarantee.
2.2.1. Successful ResponseOAuth 2.0 Token Exchange rfc
Step 7: auth.docs.example to Research sub-agentreplaces "second-delegated"
4.2. scope (Scopes) ClaimOAuth 2.0 Token Exchange rfc
Delegated token, wider than its subject tokentyp at+jwt · alg ES256 · kid docs-2026
- iss
- https://auth.docs.example
- sub
- alex
- aud
- https://api.docs.example
- client_idchanged
- research-subagent
- scopechanged
- docs:read docs:admin
- actchanged
{ "sub": "research-subagent", "act": { "sub": "orchestrator" } }- expchanged
- 1789997900
- iatchanged
- 1789996600
- jtichanged
- a3
may_actremoved- no longer in the token
7 claims differ from the previous token in this chain.
Step 8: Research sub-agent to Documents API
4. Validating JWT Access TokensJSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens rfc
Delegated token as presentedtyp at+jwt · alg ES256 · kid docs-2026
- iss
- https://auth.docs.example
- sub
- alex
- aud
- https://api.docs.example
- client_id
- research-subagent
- scopechanged
- docs:read
- act
{ "sub": "research-subagent", "act": { "sub": "orchestrator" } }- exp
- 1789997900
- iat
- 1789996600
- jti
- a3
1 claim differ from the previous token in this chain.
Step 9: Documents API to itself
The audit record says the sub-agent read documents for Alex by way of the orchestrator, and the API can refuse actors it does not expect.
4.1. act (Actor) ClaimOAuth 2.0 Token Exchange rfc
Step 10: Documents API to Research sub-agent
4. Validating JWT Access TokensJSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens rfc
Requirements this chain exercises
- No token passthrough to upstream APIsMCP server
- Audience validationMCP server