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 ID-JAG)
  • JWT access tokens (RFC 9068)

Standardizing act using 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

RFC 8693 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 sub the end-user who authorized access?
  • Is sub the workload client acting autonomously?
  • Is sub an 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.

Identity semantics and delegation semantics must be explicit protocol data, not institutional memory.

๐Ÿ”ง The Proposal: act + Entity Profiles

The approach combines existing pieces:

  • act as the explicit delegation vehicle (per RFC 8693).
  • Entity profile claims (sub_profile, client_profile) to make principal type explicit on each principal, including within nested act nodes.
  • The same model applied to both surfaces: JWT assertion grants and JWT access tokens.

The natural vocabulary for profile semantics is OAuth Entity Profiles (draft-mora-oauth-entity-profiles-00), 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:

ClaimMeaning
issIssuer asserting the principal and delegation relationship
subPrincipal whose authority is being exercised
actPrincipal currently acting on behalf of sub (when delegation exists)
sub_profilePrincipal profile value(s) for sub or for any act node

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

1
2
3
4
5
6
7
8
{
  "iss": "https://as.example.com",
  "aud": "https://api.example.com",
  "client_id": "client-123",
  "sub": "248289761001",
  "scope": "payments:write",
  "exp": 1773076800
}

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "iss": "https://as.example.com",
  "aud": "https://api.example.com",
  "client_id": "client-123",
  "sub": "248289761001",
  "sub_profile": "user",
  "act": {
    "sub": "agent-7f3c",
    "iss": "https://as.example.com",
    "sub_profile": "ai_agent"
  },
  "scope": "payments:write",
  "exp": 1773076800
}

Now the resource server can evaluate both principals explicitly. 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.

Here, an external AI assistant from home-ai.example is acting on behalf of a user to reach a specialist agent endpoint inside example.com. Three steps.

Step 1: Cross-Domain JWT Assertion Grant

The external identity provider issues a JWT assertion encoding the user and their delegated AI assistant:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "iss": "https://idp.home-ai.example",
  "sub": "user-alice",
  "sub_profile": "user",
  "act": {
    "iss": "https://idp.home-ai.example",
    "sub": "alice-planner-agent",
    "sub_profile": "ai_agent"
  },
  "scope": "agent:invoke specialist:analysis",
  "aud": "https://as.example.com/token",
  "exp": 1773076800
}

The delegation is explicit in the assertion itself: Alice authorized this agent to act for her. An authorization server (AS) receiving this assertion can resolve both principals and their types without local conventions or issuer-specific branching.

Step 2: Token Exchange

The client presents the assertion at the token endpoint using grant_type=urn:ietf:params:oauth:grant-type:token-exchange from RFC 8693, with scope=agent:invoke specialist:analysis. The internal AS validates the federation trust, applies its own access control policy, and issues a JWT access token for the specialist agent endpoint.

Step 3: JWT Access Token with Preserved Chain

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
  "iss": "https://as.example.com",
  "aud": "https://agent.example.com/specialist-agent",
  "sub": "user-alice",
  "sub_profile": "user",
  "scope": "agent:invoke specialist:analysis",
  "act": {
    "iss": "https://as.example.com",
    "sub": "domain-bridge-agent",
    "sub_profile": "ai_agent",
    "act": {
      "iss": "https://idp.home-ai.example",
      "sub": "alice-planner-agent",
      "sub_profile": "ai_agent"
    }
  },
  "exp": 1773078600
}

The outer act is the current actor (the internal bridge agent). The nested act is the delegation history (the original external agent). This follows RFC 8693 Section 4.1 exactly.

What this scenario demonstrates is why both surfaces must align. The assertion grant and the access token encode the same delegation, in the same model. The AS does not need to translate between two different actor representations as it crosses the issuance boundary. The resource server receives a token where principal types 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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
input: access_token jwt

1) Validate token (sig, iss, aud, exp) per JWT AT profile (RFC 9068).
2) Resolve subject         := token.sub
   Resolve subject_profiles:= parse_profiles(token.sub_profile)
3) If token.act present:
   actor                := token.act.sub      // current actor (RFC 8693)
   actor_profiles       := parse_profiles(token.act.sub_profile)
   evaluate policy.allow_delegate(actor_profiles, subject_profiles)
   evaluate policy.allow_actor_for_subject(actor, subject)
   evaluate policy.allow_scope_for_pair(token.scope, actor, subject)
4) If token.act absent:
   evaluate policy.allow_subject_self(token.scope, subject, subject_profiles)
5) Optionally log nested token.act.act... as delegation history for audit.
6) Enforce decision.

The three-case decision tree for relying parties becomes:

  1. act present: dual-principal policy evaluation (sub authority + act authority to act).
  2. No act, sub_profile contains user: direct user-context access.
  3. 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 delegated execution. 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.

๐Ÿ“‹ Recommendation

The OAuth WG should standardize an interoperable actor profile that:

  • reuses act from RFC 8693,
  • uses OAuth Entity Profiles vocabulary for principal typing (sub_profile) on each principal in the chain,
  • applies uniformly to JWT assertion grants and JWT access tokens,
  • defines deterministic processing expectations for AS validation and resource server authorization.

The primitive work is already done. act exists. Entity profiles are being defined. Token exchange is standardized. 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.