OAuth Token Exchange and Delegation for Agentic AI

Published
02 September 2026
by
Mark Goddard

This is the third post in a series on agentic AI security. The first, Agents Aren't People: Why AI Security Requires Workload Identity, covers the broader problem space and why workload identity is the right foundation for agentic systems. The second, OAuth Client Authentication and Registration for Workloads and Agents, covers secretless client authentication with JWT assertions and SPIFFE SVIDs, and how clients obtain identities in dynamic environments.

Agentic AI systems are increasingly built as chains of autonomous components. A user asks a web app to do something, the app invokes an AI agent, the agent calls tools exposed by one or more MCP servers, and those servers call real APIs that move money, change infrastructure, or read sensitive records. Every hop acts with some authority, and whether the whole system is safe comes down to one simple question: on whose behalf is this call being made, and who is actually making it?

Most deployments answer it badly. The user's credentials get forwarded verbatim, or worse, stuffed into a prompt, so any component in the chain can do anything the user can. Or every downstream call runs under one shared service account, and by the time a request reaches an API there is no way to tell which user triggered it, which agent acted, or whether the action was authorized at all. Either way, a multi-party interaction collapses into a single opaque principal, which is precisely what an auditor, an incident responder, or a least-privilege policy needs to be able to pull apart.

The previous posts in this series established how workloads and agents obtain identities and authenticate as OAuth clients. This post picks up from there: once an agent can prove who it is, how does it act on a user's behalf when it calls a resource server, in a way that stays tightly controlled and fully auditable?

Picking up our scenario from the previous post again: User → Web App → AI Agent → MCP Server → External API. When the agent calls the MCP server, the request should carry both the user's identity (who authorized the action) and the agent's identity (who is performing it). The OAuth 2.0 Token Exchange specification (RFC 8693) is what makes that possible, and the rest of this post is about using it correctly across a multi-hop chain.

Token Exchange for Agents

RFC 8693 defines a flow in which a client presents one or two existing tokens to a Security Token Service (STS) and receives a new token back. In practice the STS is just the token endpoint of an AS configured to accept exchange requests. Our own STS, Cofide Credex, implements the flow described here. For any reader who would like to learn more, we will link to the relevant parts of the Credex docs as we go.

A token exchange request uses the OAuth 2.0 token endpoint with a grant_type of urn:ietf:params:oauth:grant-type:token-exchange.

POST /token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=<existing-token>
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&actor_token=<agent-jwt-svid>
&actor_token_type=urn:ietf:params:oauth:token-type:jwt
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-spiffe
&client_assertion=<agent-jwt-svid>
&audience=mcp-server

RFC 8693 defines two distinct modes for conveying authority in issued tokens: impersonation and delegation.

Impersonation vs. delegation

With impersonation, the client asks the STS for a new token that keeps the original token's subject claim but drops the intermediary service's own identity. As far as the resource server can tell, the request came straight from the user; the service acting in between is invisible. That is occasionally what you want at a trust domain boundary where the downstream system has no concept of delegation, but it erases the audit trail.

With delegation, the original subject's identity is preserved and the acting service's identity is added as the act (actor) claim. The resource server gets a token where sub is the user and act is the agent, so it can make access control decisions based on both. The full delegation chain is explicit and auditable.

{
  "sub": "alice@example.com",
  "act": {
    "sub": "spiffe://example.com/ai-agent"
  },
  "aud": "https://mcp.example.com",
  "scope": "read:data",
  "exp": 1712534460
}

For agentic AI, delegation is the right default. The agent isn't impersonating the user; it is acting on the user's behalf, unsupervised and semi-autonomous. That distinction is important for: audit ("the AI agent accessed this resource, acting for Alice"), access control ("the agent may only act on behalf of an authorized user"), and least privilege (the agent's token can be scoped to exactly the operations the task needs, independently of the user's broader permissions).

As an example, Alice asks an AI assistant to reconcile last month's invoices, and the agent calls a finance API to do it. Under impersonation, the API sees a request from Alice and nothing else, so if the agent is compromised, or just misbehaves and starts issuing refunds, the logs blame Alice and the API has no grounds to refuse, because Alice is allowed to issue refunds. Under delegation, the API sees that the reconciliation agent is acting for Alice, can refuse write operations the agent isn't entitled to perform regardless of Alice's own permissions, and records exactly which agent did what. When an incident review later asks "what did this agent do, and who authorized it?", the answer is already sitting in the audit trail.

Impersonation still has a place: crossing into a legacy external domain where the downstream AS and API have no idea what delegation means. You drop the agent identity there deliberately, because the downstream system can't use it anyway, while keeping the user identity for authorization. It's a call you make on purpose at the trust domain boundary, not a default you reach for throughout the chain.

Delegation Deep Dive

Wire-level mechanics

Back to the running example: User → Web App → AI Agent → MCP Server → External API. An agent that has been handed a user access token (from the web app) performs a token exchange with the local STS to get a delegated token for the MCP server:

POST /token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=eyJ...  (user's access token, sub: alice@example.com)
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&actor_token=eyJ...    (agent's JWT-SVID, sub: spiffe://example.com/ai-agent)
&actor_token_type=urn:ietf:params:oauth:token-type:jwt
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-spiffe
&client_assertion=<JWT-SVID>
&audience=https://mcp.example.com
&scope=read:data

‍The STS authenticates the agent (using SPIFFE client auth as described in the previous post, validates both tokens, applies its delegation policy, and returns a new access token:

{
  "iss": "https://as.example.com",
  "sub": "alice@example.com",
  "act": {
    "sub": "spiffe://example.com/ai-agent"
  },
  "aud": "https://mcp.example.com",
  "scope": "read:data",
  "iat": 1712534400,
  "exp": 1712534760
}

The MCP server validates this token and can see both who authorized the action (sub: alice) and who is performing it (act: {sub: spiffe://.../ai-agent}).

Building the chain across hops

If the MCP server then needs to call another service in the same trust domain, it can extend the delegation chain. It presents the delegated token it received as the subject, and its own JWT-SVID as the actor:‍‍

POST /token

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=eyJ...  (delegated token: sub=alice, act=ai-agent)
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&actor_token=eyJ...    (MCP server's JWT-SVID)
&actor_token_type=urn:ietf:params:oauth:token-type:jwt
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-spiffe
&client_assertion=<JWT-SVID>
&audience=https://internal-api.example.com

The resulting token carries a nested act claim, a chain of actors:

{
  "sub": "alice@example.com",
  "act": {
    "sub": "spiffe://example.com/mcp-server",
    "act": {
      "sub": "spiffe://example.com/ai-agent"
    }
  },
  "aud": "https://internal-api.example.com"
}

Read from the innermost act outward and you have the full delegation chain: the AI agent acted on Alice's behalf, then the MCP server acted on the AI agent's behalf. RFC 8693 is clear that only the current actor should drive access control decisions. The prior actors are informational, mainly useful for audit trails.

The MCP scenario end-to-end

The diagram below shows the agent obtaining a delegated token via RFC 8693 token exchange and then using it to authenticate to the MCP server. The MCP server validates both the user identity (sub) and the acting agent (act) before it runs the tool. Its own downstream call to an external API needs a further token exchange to extend the chain, which we get to in the cross-domain section below.

AS policy requirements

The STS is not a passive token transformer. It has to enforce real policy, otherwise it's just a token-laundering service. Credex gates every exchange behind an exchange policy that determines which exchanges are allowed; if no policy matches, the exchange is denied. A policy answers three questions:

  • Who may delegate? Not every caller can perform a delegation exchange. The STS should enforce that only authorized services (identified by their SPIFFE IDs) can act as actors.
  • To whom may they delegate? An agent may only act on behalf of users who have consented. The STS validates that the subject token's audience or scope permits delegation.
  • Which scopes may be granted? The delegated token's scopes should be at most the intersection of what the user token permits and what the actor is permitted to request, enforcing least privilege even if the user has broader permissions.

Cross-Domain Delegation: Identity Chaining

The hardest hop is the last one: the MCP server needs to call an external API in a different trust domain. That API has its own AS, and the local STS's tokens mean nothing to it.

There are two cases to handle, and they call for different techniques.

Scenario A: the external AS is legacy

Most external APIs and their ASes were built with no notion of delegation. They expect a plain OAuth access token with a sub claim for the resource owner, and any act claim gets ignored or rejected.

The move here is impersonation to cross the trust boundary, paired with an RFC 7523-compliant JWT authorization grant to federate identity between the local STS and the external AS.

The diagram below illustrates this cross-trust-domain flow:

The impersonation exchange in Step A deliberately drops the act claim (by omitting the actor_token parameter in the exchange request), so the agent identity is gone. The resulting JWT grant represents only the user, which is exactly what the external AS expects. All the external AS has to do is trust the local STS as an identity provider: register it as a trusted issuer and point at its JWKS endpoint so it can validate JWT signatures. That's standard identity federation, with no delegation-specific setup required. Once the trust is in place, the external AS issues its own access token for the user.

The delegation context isn't lost entirely. It survives in the STS audit events, which record the impersonation exchange along with the full delegation chain of the incoming subject token, and policy enforcement has already happened before the request ever reaches the external system.

Scenario B: the external AS supports delegation

When the external AS is delegation-aware, say another Cofide trust domain or a partner system that has adopted the relevant standards, the full chain can survive the boundary crossing.

The emerging standard for this is draft-ietf-oauth-identity-chaining. Built on RFC 8693 and RFC 7523, it defines a flow in which:

  1. The local STS issues a JWT authorization grant for the external AS, embedding the act chain
  2. The MCP server uses that grant to obtain an access token from the external AS via the RFC 7523 JWT bearer grant flow
  3. The external AS, aware of the act claim, mints a token that preserves the delegation chain

The trick is that the JWT grant isn't just a credential, it's a carrier for the delegation chain. The external AS validates the grant's signature against the local STS's JWKS endpoint, pulls out the act claim, and re-mints its own access token carrying the same actor information. Nothing extra is needed on the external side; the AS sees both the user and the full chain of acting services in a single token it already knows how to validate.

The full cross-trust-domain delegation-aware flow looks like this:

What about ID-JAG?

The draft-ietf-oauth-identity-assertion-authz-grant (ID-JAG) proposal, championed by Okta and shipped in Okta and Auth0 as Cross-App Access (XAA), is a related effort. It profiles identity chaining for SSO environments where applications share an identity provider, such as an enterprise IdP. It takes an OIDC ID token as the subject token input, and introduces a new OAuth token type (urn:ietf:params:oauth:token-type:id-jag).

ID-JAG works well in a controlled environment where both sides implement it. The enterprise IdP has to be able to issue the grant, the Resource Authorization Server has to implement the processing rules for consuming it, and the client has to drive both legs: the token exchange at the IdP and the JWT bearer redemption at the Resource AS. However, the Resource AS has to already rely on that same IdP for SSO and subject resolution for the user in question, which breaks in more hetergenous scenarios. For delegation chains that might terminate at any arbitrary OAuth-protected API, draft-ietf-oauth-identity-chaining with RFC 7523 JWT grants is the more broadly applicable path: it degrades gracefully, and the transport mechanism is already widely understood.

The pragmatic path today

Support for ID-JAG is growing but it does not yet have broad production support, so in practice the choice between the two scenarios comes down to what the external AS happens to support.

If it is delegation-aware, use Scenario B. The act chain is preserved and the audit trail stays complete across the trust boundary.

If it is legacy or delegation-unaware, use Scenario A. The agent identity is dropped at the boundary, but the user identity survives and the delegation context is recorded in the local STS audit log.

There's also a middle path when the external AS supports RFC 7523 but not the full identity chaining draft: include the act claim in the JWT grant the local STS issues, and configure the external AS to copy it through into the tokens it mints via custom claim mapping. In Keycloak that's a protocol mapper, in Auth0 an Actions script, in Credex an external hook that enriches the outbound token's claims before it is minted; same idea in each case. The act claim is there for systems that understand it, and the token degrades gracefully to a plain user token for systems that don't.

Scenario B with OAuth Identity Chaining is where the ecosystem is heading, and all three approaches are compatible stepping stones toward it.

Summary

Putting SPIFFE client authentication (from the previous post) together with OAuth 2.0 token exchange closes the loop on what we set out to do at the start of the series. At every intra-domain hop, each token carries user and actor information via RFC 8693 delegation, scoped to the specific resource being called. At the external boundary the user identity is always present, the agent identity is preserved wherever the downstream AS supports claim passthrough, and it's recorded in the STS audit log when downstream support is lacking. Either way, the audit trail is complete at the local trust domain boundary, and policy enforcement happens before the request crosses it.

Getting from a naive implementation to a fully auditable delegation chain is an incremental journey, not a rewrite. The foundations, SPIFFE identities, JWT client assertions, and token exchange with delegation, are available today in production AS implementations. SPIFFE client authentication is in preview and on a clear path to RFC status, and the cross-domain delegation drafts are maturing fast, with real implementations starting to appear.

We keep writing about this because the gap between what teams are shipping and what the standards already make possible is widening. Agents are being wired into production right now, and the default identity story is still shared service accounts and forwarded credentials. The building blocks to do better exist, but they're scattered across several specs at different levels of maturity, and assembling them correctly across a multi-hop, multi-domain chain is genuinely fiddly. Getting workload identity right up front is a lot cheaper than retrofitting it after an agent has spent a year quietly acting as a shared super-user, and that foundational workload identity layer is exactly what Cofide provides.

Coming Soon: Cofide Credex

At Cofide we're building Credex as the Authorization Server and Security Token Service that implements this whole stack: SPIFFE-native client authentication, delegation-aware token exchange with policy enforcement, and the cross-domain bridges that connect agent-first architectures to the broader ecosystem of OAuth-protected services. Credex is in feature preview today. The use cases docs walk through the agentic AI delegation scenario from this post end to end. The exchange types guide covers the supported exchange types. The policy guide describes the policy model in detail. There's more on Credex coming in the next post in the series.

If you're building agentic AI systems and want to talk through how Cofide can help you put these patterns into production, get in touch.

References

Layer Standard Status
Workload identity SPIFFE / SPIRE Production-ready
Token exchange RFC 8693 Production-ready (AS support varies); Preview (Cofide Credex)
OAuth client auth with SPIFFE draft-ietf-oauth-spiffe-client-auth Preview (Cofide Credex, Keycloak, Curity)
Cross-domain delegation draft-ietf-oauth-identity-chaining Draft
Enterprise Cross-App Access (XAA) draft-ietf-oauth-identity-assertion-authz-grant Draft; Implementation (Okta, Auth0, Ping); used by MCP Enterprise-Managed Authorization extension

Ready to connect?

Our team is ready to walk you through how Cofide can help you to securely connect workloads with confidence.