Agent Delegation Identity: Building Auditable Chains for Tool Authorization
This article explains why AI agents need distinct delegated identities with auditable chains instead of shared secrets or user impersonation, detailing OAuth 2.0 Token Exchange (RFC 8693) act claims, nested delegation chains as audit evidence not authorization, MCP's resource indicator requirements, and practical patterns from Gravitee and Microsoft Entra Agent ID.
At 3 a.m., a customer‑service agent automatically approves an excessive refund. The next day, logs show only that a service principal called the refund API — no record of who authorized it, which user the agent represented, or whether the request came directly from the user or via an upstream orchestrator. The root cause is not missing logs but an absent identity model.
Most teams still provision agent credentials with a long‑lived secret shared across all agents, so every call appears to come from the same middle‑tier identity. Once an agent acts on behalf of a specific user, that model collapses.
Three Flawed Approaches
Shared secret. The simplest method: bake a client_secret or long‑lived API key into the agent image or .env file. The blast radius is huge — one leaked log or a file missed by .gitignore exposes the entire tenant. Post‑incident, you cannot distinguish whether the agent acted autonomously or on user instruction; auditability breaks at this layer.
Pass the user token through unchanged. This seems most faithful to the user, but the agent receives the user’s full permissions. Every downstream tool sees the user as the caller. If any intermediate component forwards the token, a classic confused‑deputy attack becomes possible: a low‑privilege service uses the user’s token to call a higher‑privilege service, which accepts it because the token is valid. The user never consented to that call, and no link in the chain can detect it.
Let the agent impersonate the user. The agent assumes the user’s identity, making them indistinguishable in the permission context. Functionally it works, but the audit trail disappears — you can no longer tell which operations the user clicked and which the agent decided on its own.
All three approaches sidestep the fact that an agent is an independent principal. Without that premise, permission scoping, auditing, and revocation are impossible.
Delegation vs. Impersonation: The Difference Is Not Wording
OAuth 2.0 Token Exchange (RFC 8693) draws a sharp line. Impersonation grants A all of B’s rights in the same context, making A and B indistinguishable. Delegation is different: A retains its own identity; B grants A the right to act on B’s behalf, and every action is performed by A as B’s agent.
In protocol terms, delegation uses two tokens: subject_token identifies on whose behalf the action is taken, and actor_token identifies who is actually acting. The exchanged token carries an act (actor) claim.
A serialized example:
{
"iss": "https://as.example.com",
"sub": "user:u_8823",
"aud": "https://mcp.example.com/refund",
"scope": "refund:create",
"exp": 1790000000,
"act": {
"sub": "agent:cs-triage",
"act": { "sub": "agent:orchestrator" }
}
}This token reveals three things at a glance: it is scoped to the refund resource, limited to refund:create, and the call chain shows the orchestrator handed off to cs‑triage before the request was made.
The key benefit is that each hop gets its own token. The orchestrator should not hold a token that can directly refund; it should exchange for a token only sufficient to call the downstream agent. The downstream tool service then exchanges for a token only sufficient to access its own data store. If any link is compromised, the leaked credential is a narrow‑scope, short‑lived, resource‑bound token — not a tenant‑wide long‑lived pass.
The act Chain Is Nested, But It Is Not an Authorization Basis
Delegation chains are expressed by nesting: the outer act is the current actor, inner ones represent previous actors, with the earliest buried deepest. It might seem that a longer chain implies more complete authority — the opposite is true. The specification states clearly: the consumer must consider only the top‑level claims and the current actor; nested historical actors are for reference only and carry no authorization weight.
This boundary is critical. The delegation chain’s purpose is audit evidence, not authorization basis . You can use it to reconstruct how a call originated, but you must not grant access because a trusted principal appears somewhere in the chain. A service that implements “if a trusted principal appears in the chain, allow” has turned a pure audit structure into a permission structure, creating a bypass.
Complementing this is the may_act claim, which constrains delegation beforehand. It asserts that a party is permitted to become an actor for another party. The authorization server can check the may_act claim on the subject token to decide whether to accept the exchange.
MCP Closes the Token‑Forwarding Path
At the tool‑call layer, the specification changes are more direct than many realize. Several MUST‑level constraints together forbid the second flawed approach (passing the user token through).
MCP clients must implement RFC 8707 Resource Indicators: both authorization and token requests must include a resource parameter using the MCP server’s canonical URI. This is required regardless of whether the authorization server supports it.
MCP servers must verify that the token was issued specifically for them (audience binding); tokens not issued for that server are rejected.
Most critically, MCP servers must not accept or forward any other token. Forwarding an upstream token is defined as non‑compliant behavior, not merely a risky practice.
A 401 challenge may include a scope parameter; the client should request the minimum required scope. On insufficient_scope, the client performs a step‑up flow, combining the new challenge’s scope with previously granted scopes to avoid losing other permissions.
Together, these rules require every hop to obtain its own token rather than shining a single flashlight from end to end.
Implementation gaps exist. Some SDKs leave audience verification to the developer’s verify_token function, with no framework‑level check of the aud claim. The spec says MUST, but the framework does not enforce it. This caused a real debugging ordeal: everything appeared to work until the token was presented to another service.
What Configuration Looks Like
Beyond the spec, production shapes vary. Two contrasting products illustrate the range.
One type embeds token exchange into the identity platform. Gravitee’s Access Management makes Token Exchange a Security Domain capability, enabled per application. Notable controls:
Impersonation and Delegation are separate switches; disabling impersonation prevents agents from hiding their identity.
Allowed Actor Types restricts what type of actor_token is accepted; cross‑domain agents require certificate‑based signature verification.
Delegation Depth limits nesting levels (e.g., A → B → C stops there) to prevent infinite loops.
Downscoping requires that requested scopes be a subset of the user’s granted scopes — read‑only grants cannot be exchanged for write access, enabling task‑level authorization.
A valuable side effect: because the user’s original token and the exchanged agent token are linked, revoking the original token cascades, invalidating the entire delegation chain. Revocation, a distributed problem in delegation models, becomes a single local operation.
The other type moves credential handling entirely out of the agent process. Microsoft Entra Agent ID’s sidecar mode runs a separate container alongside the agent, handling client‑credential exchange, on‑behalf‑of flows, and token lifecycle. It exposes only /AuthorizationHeader (to fetch a token) and /DownstreamApi (to fetch a token and proxy the call). The sidecar does not expose host ports; only the co‑located agent container can request tokens. Credential sources are transparent to agent code: during development a ClientSecret is used, in production it swaps to SignedAssertionFromManagedIdentity without changing a line of agent code.
This design solves a practical problem: no matter how you secure prompts or whitelist tools, if a secret lives in the agent’s memory, one injection or accidental log print can exfiltrate it. Isolating credential handling to another process shifts the risk from “write correct code” to “enforce network boundaries.”
Reading the spec makes these seem like trivial configuration items. In reality, each spec sentence often translates to multiple quarters of engineering work.
OAuth Handles Identity, Not Actions
A common misconception needs unpacking. After implementing token exchange, act claims, and audience binding, you have a system that answers “who is this, on whose behalf, and what class of permission was granted.”
It does not answer whether this specific agent should be allowed to make this specific call on this resource right now. That is an authorization decision, belonging to RBAC, ReBAC, or ABAC layers, and it must be enforced at the granularity of each tool call.
The combined principle: OAuth owns identity; the policy layer owns every action. Pushing all decisions into token scopes leads to two failure modes — either scopes are extremely coarse (a single refund:* rules everything) or they explode into an unmaintainable mess. Both appear frequently in post‑mortems.
Four Questions to Answer Before Going Live
If your agent system will touch real permissions, these four questions are unavoidable:
Is the agent an independent identity? If it still runs under a shared middle‑tier service principal, the answer is no. Until this is fixed, everything else is a patch.
Does every hop exchange for a new token? Audit your call chains: are any paths still forwarding the upstream token unchanged? The spec now explicitly forbids this, and architecturally it is the only lever for permission convergence.
Can audit logs reconstruct the full delegation chain? Take a real multi‑agent call and try to trace which user’s request, via which agents, triggered which concrete tool. If you can answer in five minutes, the model works.
Does every tool call have an independent policy decision? If not, all access control is crammed into the token‑issuance moment; runtime context — caller identity, resource, anomalous timing, call frequency — never participates in the decision.
Agent identity is not like prompt tuning with immediate feedback. It resembles pre‑service‑mesh inter‑service auth: when scale is small, agent count low, and permissions narrow, shared secrets work. The real cost arrives when the first agent gains excess permissions and your logs show nothing.
Deciding whether to address this now is more important than figuring out how.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architecture Development Notes
Focused on architecture design, technology trend analysis, and practical development experience sharing.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
