OAuth has a delegation visibility problem.
In many deployments, a token tells you that someone is acting, but not clearly who is acting for whom. Delegation is implied by context, encoded in sub by convention, or inferred from client_id heuristics. That might be acceptable inside a single product boundary. It is not acceptable for standards-based interoperability.
The fix is not a new claim. The primitives already exist. What is missing is a shared profile that applies them consistently.
The OAuth WG should standardize one actor model across both:
- JWT assertion grants (RFC 7523 and profiles built on top of it, including Identity Assertion JWT Authorization Grant (ID-JAG))
- JWT access tokens (RFC 9068)
Standardizing act using OAuth Entity Profiles (Entity Profiles) would make delegation and principal type explicit and machine-processable at both surfaces.
๐ Where the Current Specs Leave a Gap
act Exists, But Is Not Profiled Where It Matters
OAuth 2.0 Token Exchange (Token Exchange) defines act for token exchange. That is a good start. But the ecosystem treats actor semantics as optional and context-specific. Two standards-compliant implementations can still disagree about delegation meaning when a token crosses a domain boundary.
The gap runs deeper at the assertion boundary. JWT assertion grant processing (RFC 7523 and related profiles, such as ID-JAG) does not require a typed, interoperable actor model. So delegation introduced at the federation boundary does not survive into token exchange or downstream access tokens in any consistent way.
JWT Access Tokens Overload sub
In most RFC 9068 deployments today, sub is doing too much:
- Is
subthe end-user who authorized access? - Is
subthe workload client acting autonomously? - Is
suban AI agent acting on someone’s behalf?
This is not just an OAuth plumbing question. As explored in You Don’t Give Agents Credentials. You Grant Them Power of Attorney., the identity and delegation semantics carried in tokens are the foundation on which any meaningful authority governance for agents has to be built. You cannot govern what you cannot see.
When a resource server cannot answer that question from the token alone, it cannot reliably distinguish:
- direct subject access,
- user-delegated client access, or
- non-user workload or agent access.
Some implementations fall back on client_id == sub to infer “client acting as itself.” That heuristic can work in simple single-hop cases, but it does not survive delegation chains and it says nothing about what kind of principal either party is.
That ambiguity creates policy drift. Authorization decisions diverge, audit trails lose fidelity, and security reviews turn into arguments over interpretation.
Actor Identity Is Asserted, Not Bound
Even after act is populated, the actor claim is just a string. There is no cryptographic guarantee that the entity presenting the token is the same entity identified in act.sub. A stolen token can be replayed by a different actor, and the resource server has no way to tell.
Proof-of-Possession Key Semantics for JWTs (RFC 7800) defines cnf to bind a token to a holder’s key. The jkt member carries a SHA-256 JWK Thumbprint of the holder’s public key. DPoP-Protected JWT-Secured Authorization Grants (PoP JAG) and Identity Assertion JWT Authorization Grant (ID-JAG), Section 8.4 both support sender-constraining the current presenter: the assertion includes cnf.jkt, the client presents a DPoP proof, and the authorization server validates that the proof’s public key matches the asserted thumbprint. No matching proof, no token.
Key binding is not required for every delegation scenario. Many internal workload deployments are fine with bearer semantics. But when an actor presents a DPoP key binding at the token endpoint, the issued token should carry that actor’s key at the top-level cnf. On the next exchange hop, the newly issued token should repeat that pattern for the next presenter while preserving prior actor keys in the act chain as delegation history. If a cnf.jkt established at the assertion grant boundary is silently dropped in the issued access token, the resource server cannot enforce a sender constraint the authorization server already accepted.
Identity semantics and delegation semantics must be explicit protocol data, not institutional memory. Key binding makes actor identity verifiable, not just visible.
๐ง The Proposal: act + Entity Profiles
The approach combines existing pieces:
actas the explicit delegation vehicle (per Token Exchange).- Entity profile claims (
sub_profile,client_profile) to make principal type explicit on each principal, including within nestedactnodes. - Top-level
cnfwithjktfor the current token presenter when that presenter established a DPoP key binding, consistent with PoP JAG, ID-JAG Section 8.4, and RFC 7800. Prior actor keys may be preserved inside nestedactnodes as delegation history, but only the top-levelcnfis the live sender constraint the resource server enforces. - The same model applied to both surfaces: JWT assertion grants and JWT access tokens.
The natural vocabulary for profile semantics is Entity Profiles, which defines sub_profile and client_profile. The actor-chain profile should align with that vocabulary rather than creating a parallel taxonomy. In the current draft, standardized values include user, device, native_app, web_app, browser_app, service, and ai_agent, and profile claims are space-delimited strings when multiple values are present.
For interoperable processing across trust domains, each principal and delegation relationship must be explicit. The resulting contract is:
| Claim | Meaning |
|---|---|
iss | Issuer asserting the principal and delegation relationship |
sub | Principal whose authority is being exercised |
act | Principal currently acting on behalf of sub (when delegation exists) |
sub_profile | Principal profile value(s) for sub or for any act node |
cnf.jkt | SHA-256 JWK Thumbprint of the current presenter’s key; carried at the top level when the current actor established a DPoP binding |
act.cnf.jkt | Optional preserved thumbprint for a prior actor in the delegation chain; useful for audit and provenance, not live sender-constrained enforcement |
No new grant type. No new token type. A shared profile and processing rules built on what already exists.
๐ก What This Looks Like in Practice
JWT Access Token: Before
| |
A resource server cannot determine whether sub is a user, a client instance, or an agent identifier. It cannot tell whether this is direct access or delegated access. That ambiguity is exactly what a standard should eliminate.
JWT Access Token: After
| |
Now the resource server can evaluate both principals explicitly. Here the current actor established a DPoP key binding during token exchange, so cnf.jkt is present at the top level and the agent must present a matching DPoP proof. If no key binding was established, top-level cnf is absent and bearer semantics apply. No heuristics. No local conventions.
๐ The Canonical Cross-Domain Case
The most important scenario to get right is cross-domain delegation, where a federation boundary is crossed and then token exchange carries the principal chain downstream.
planner-agent is an external AI agent from assistant.example acting on behalf of Alice. It uses hotel-tool at tools.example, which must in turn call an internal backend at inventory.example, a service the agent cannot reach directly.
The flow covers four steps. Alice signs in and the agent receives a DPoP-bound refresh token. The agent exchanges that for a cross-domain ID-JAG. The agent presents the ID-JAG to obtain a tool access token, still bound to the agent’s key. Finally, hotel-tool performs its own Token Exchange to reach the backend, shifting the live sender constraint to the tool’s key. That last step is the point: the originating agent and the current presenter are not the same actor on every hop.
| Role | Entity Type | Subject ID | OAuth Client | Key Binding (jkt) |
|---|---|---|---|---|
| User | user | user-alice | No | n/a |
| AI agent | ai_agent | planner-agent | Yes | NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs |
| Tool | service | hotel-tool | Yes | R3g0XuBFHRTqAq3HyOVSLWLBXb-RypXz6NsKrOSqMmE |
Step 1: Authorization Request
Alice is using planner-agent and signs in with assistant.example. The agent sends her through a standard OIDC authorization flow, using authorization code binding via dpop_jkt so the resulting code is tied to the agent’s key.
The authorization request looks roughly like this:
| |
After Alice authenticates, the agent redeems the code at the token endpoint and proves possession of the same key with DPoP:
| |
The response below is abbreviated but still shows the surrounding token fields for realism. The artifact that matters for the next step is the DPoP-bound refresh token, which the agent will later use to mint an ID-JAG for cross-domain access.
| |
At this point the agent has a user-authenticated session and a refresh token bound to its key, but the cross-domain delegation semantics are not yet packaged into a JWT grant another authorization server can consume directly.
Step 2: Refresh Token to ID-JAG
planner-agent uses that refresh token as the subject_token in Token Exchange to obtain an ID-JAG for hotel-tool in another trust domain.
The request might look like this:
| |
That exchange does not change the delegation model. It packages the delegated user and acting agent into an ID-JAG that another authorization server can validate directly.
The token exchange response might look like this:
| |
Even though the issued artifact is an ID-JAG rather than an access token, Token Exchange still returns it in the access_token response field and relies on issued_token_type to tell the client what it actually received.
The resulting ID-JAG might look like this:
| |
The delegation is explicit in the ID-JAG itself: Alice authorized planner-agent to act for her. planner-agent established a DPoP key binding when the grant was issued, so cnf.jkt appears at the top level. That binding remains part of the chain until a later token exchange mints a new access token for a different current presenter.
Step 3: ID-JAG to Tool Access Token
planner-agent, not hotel-tool, presents the ID-JAG to auth.tools.example. Because the grant is DPoP-bound, the agent uses the JWT-DPoP grant and proves possession of the same key that is referenced in the ID-JAG’s top-level cnf.jkt.
The request might look like this:
| |
The tools authorization server validates federation trust, validates the ID-JAG and the agent’s proof of possession, and issues an access token for hotel-tool still bound to planner-agent’s key.
That tool access token might look like this:
| |
The current actor is still planner-agent, because it is the entity presenting this access token to hotel-tool. The live sender constraint remains the agent’s top-level cnf.jkt. At this point there has been no key transition yet.
Step 4: Tool Token Exchange for Backend Service
hotel-tool receives the inbound access token when planner-agent calls it. To reach inventory.example, it performs one more Token Exchange using that token as the subject_token and presenting its own DPoP proof. This is where the live sender constraint shifts from the agent’s key to the tool’s key.
The request might look like this:
| |
The backend authorization server validates the inbound tool token, preserves the actor chain, validates hotel-tool’s proof of possession for the new audience, and issues a backend access token bound to the tool’s key.
The resulting backend access token might look like this:
| |
The current presenter is now hotel-tool, so the live sender constraint is the tool’s top-level cnf.jkt. The nested act chain preserves planner-agent and the upstream key that established the original delegation. This is the key transition the profile needs to make explicit: each new access token binds only the current presenter, while the chain preserves who previously acted for whom.
What this scenario demonstrates is why both surfaces must align. The ID-JAG and the downstream access tokens encode the same delegation, in the same model. The first cross-domain hop keeps planner-agent as the current presenter. The later backend hop changes the current presenter to hotel-tool and mints a new top-level sender constraint without losing the upstream actor chain. The AS does not need to translate between different actor representations as it crosses issuance boundaries. The resource server receives a token where principal types and delegation history are unambiguous across the full chain.
If assertion grants and access tokens use different delegation conventions, the ambiguity just moves one layer up. The alignment is the point.
โ๏ธ Resource Server Processing
With a standardized model, the resource server can implement stable, issuer-agnostic logic:
| |
The three-case decision tree for relying parties becomes:
actpresent: dual-principal policy evaluation (subauthority +actauthority to act).- No
act,sub_profilecontainsuser: direct user-context access. - No
act, non-user subject profile: non-delegated workload or agent self-access.
client_id may still be an input, but it is not the delegation model. It cannot represent chained delegation and does not type either principal.
This is the operational value of the profile: the resource server does not branch on issuer-specific convention to understand delegation semantics. Authorization, audit, and policy enforcement work the same way across implementations.
๐ก๏ธ Deployment and Backward Compatibility
This does not require a flag day.
Existing deployments that only understand subject-only tokens continue to work. Explicit act is additive, and consumers that do not understand it can ignore it (with appropriate policy defaults). Discovery or profile metadata can signal capability for implementations that want to negotiate.
The goal is not disruption. The goal is ending private delegation semantics for new and federated deployments where interoperability actually matters.
๐ Caveat: Delegation Chain Is Not Execution Identity
A nested act chain tells you who claims to act for whom. It does not prove that the current request is a valid continuation of the originating execution.
The same delegation chain can appear in multiple unrelated executions. If each presenter holds the bound key for its hop, each presentation can still be valid under proof of possession. Sender-constraining answers “who is allowed to present this token,” not “which execution produced this request.”
From a JWT, you can answer “who signed this” and, with act plus cnf, “who is acting for whom” and “who proved possession of the current key.” You cannot answer “which execution is this” unless some separate execution identifier or continuity mechanism is standardized and propagated alongside the delegation chain.
This proposal does not address execution identity or execution continuity. Its scope is limited to making delegation semantics and current presenter key binding explicit and interoperable across assertion grants and JWT access tokens.
๐ Recommendation
The OAuth WG should standardize an interoperable actor profile that:
- reuses
actfrom Token Exchange, - uses Entity Profiles vocabulary for principal typing (
sub_profile) on each principal in the chain, - uses top-level
cnf.jktfor the current presenter when a token is sender-constrained, while allowing prior actor keys to be preserved inside nestedactnodes as delegation history, - applies uniformly to JWT assertion grants and JWT access tokens,
- defines deterministic processing rules for AS validation and resource server authorization, including DPoP proof verification against top-level
cnf.jktwhen present.
The primitive work is already done. act exists. Entity Profiles are being defined. Token Exchange is standardized. DPoP key binding for the current presenter is already specified in JWT grant work and in ID-JAG Section 8.4. What is needed is the profile that stitches them together across both token surfaces, makes the processing rules explicit, and ends the cross-profile ambiguity that has so far been left to implementer judgment.
Until that profile exists, delegation will remain a private convention masquerading as a standard. Two implementations can be fully RFC-compliant and still disagree about who is acting for whom.
That is not an acceptable baseline for cross-domain identity, and it is the token-layer prerequisite for anything more ambitious. Governing delegated agent authority, enforcing mandate lifecycles, propagating revocation signals: none of it is tractable if the delegation relationship is invisible in the token.
When “who is acting for whom” is portable protocol meaning rather than local folklore, the ecosystem gets better federation, better authorization policy, and better audit trails. That is worth standardizing.
Changelog
2026-03-18: Clarified sender-constrained semantics so the current presenter uses top-levelcnf.jktand prior actor keys are preserved only as historical delegation context inside nestedactnodes.2026-03-18: Added explicit alignment to ID-JAG Section 8.4 for sender-constraining the current presenter while keeping nested prior-actor keys as profile-specific history.2026-03-18: Reworked the canonical cross-domain example around an AI agent, a cross-domain tool, and a backend service, with an end-to-end flow from OIDC sign-in to ID-JAG issuance to downstream token exchanges, plus a cast table covering role, entity type, subject ID, OAuth client status, and key binding.2026-03-18: Added an explicit caveat that this proposal does not address execution identity or execution continuity.