The series has argued for bounded agent authority: purpose, scope, conditions, and lifecycle as a governed artifact rather than something implied by credentials. The protocol challenge is how to encode that mission in a way that crosses authorization server boundaries, holds up across trust domains, and gets enforced at the right layer.

The instinct is Rich Authorization Requests (RAR, RFC 9396). Declare the mission in authorization_details, issue a scoped access token, propagate it downstream. This works well in a single authorization server (AS) deployment. It runs into a structural boundary as soon as the mission spans multiple resource servers.

๐ŸŽฏ The Audience Problem

OAuth access tokens are audience-bound. A token issued for https://calendar-api.example.com/ is not valid at https://email-api.example.com/. This is intentional: audience restriction prevents token replay across services.

A realistic agent mission may need Calendar, Email, Documents, and a CRM. Each is governed by a separate AS, potentially across organizational domains. RAR can shape each access token’s permissions. It cannot carry the mission itself across those AS boundaries in a uniform way. The agent needs a separate authorization flow at each AS, with each AS evaluating mission context independently under its own policy. Compounding this, authorization_details types in RAR are deployment-specific strings with no standard registry. A calendar AS and an email AS that each define their own types cannot share mission context even if both implement RAR correctly.

An agent mission that spans four resource servers needs four separate authorization flows, with no standard way to express that all four are part of the same governed task.

Cross-domain token exchange exists for this. RFC 8693 allows an AS to exchange a subject token for a token in a different domain, and RFC 9396 Section 4.1 allows authorization_details to travel through that exchange. The OAuth Identity Chaining draft profiles this pattern for propagating identity across AS boundaries. The challenge is not the mechanism. It is that each target AS needs bilateral trust with the source AS, must understand the specific authorization_details types being propagated, and must implement consistent claim semantics. Within a single organization’s infrastructure, this works. Across the vendor and SaaS ecosystem an enterprise agent actually operates in, per-AS bilateral trust negotiation does not scale reliably.

๐Ÿ”Œ Authentication Intent: A Different Layer

The OpenID Connect Client Context draft takes a different approach. Instead of encoding mission at the authorization layer (where the question is “what should this token permit?”), encode it at the authentication layer (where the question is “why is this authentication happening, and under what constraints?”).

The client_context parameter carries typed context objects in the authorization request. The OpenID Provider (OP) evaluates them against registered policy, enforces constraints at authentication time, and returns the applied context in the ID Token as a cryptographic record of what was authorized.

For agents acting on behalf of users, the OP is the natural enforcement point. It is where the user is present. It is where authentication policy has full authority. It is the entity that decides whether to issue any credential at all. Encoding mission intent here means constraint enforcement happens before any token exists, not after.

This argument applies specifically to user-delegated agent scenarios where a human principal is in the loop. Automated workloads with no human initiator use client credentials, not OIDC authentication, and are out of scope here. client_context is an extension of interactive OIDC, not a replacement for OAuth 2.0 client authentication.

That does not mean the ID Token should become a general-purpose API credential. The narrower claim is that the OP-issued mission record can be the stable input to downstream assertion issuance. The portability is in the approved mission context and the OP’s policy evaluation, not in treating the same token as something every resource server should accept directly.

The OIDC claims request parameter (Core Section 5.5) is not the same thing: it requests specific user attributes in the ID Token or UserInfo response. client_context is an input shaping how authentication occurs, not a request for output claims.

๐Ÿงฌ The Purpose Context

The context type that matters for agents is purpose. A purpose context is a structured declaration of what task is being performed, on whose behalf, under what constraints.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
  "type": "purpose",
  "kind": "https://example.com/purposes/schedule-meeting",
  "id": "inv_a1b2c3d4",
  "justification": "mandate:mnd_7x9k2p",
  "display": {
    "title": "Schedule Meeting",
    "description": "Scheduling a meeting with the Engineering team"
  },
  "params": {
    "calendar": "engineering@example.com",
    "date_range": "2026-03-10/2026-03-17"
  },
  "actor": {
    "type": "agent",
    "id": "calendar-agent-v2",
    "sub": "user-123",
    "delegation": [
      { "type": "user", "id": "user-123" }
    ]
  },
  "constraints": {
    "expires_at": "2026-03-10T17:00:00Z",
    "max_duration": 900,
    "max_uses": 1,
    "delegation_depth": 1
  }
}

kind is a URI identifying the purpose class. The OP maps it to authentication policy: step-up requirements, session lifetime caps, and allow-list validation. URI namespacing means each agent platform owns its purpose catalog without a central registry.

id uniquely identifies this invocation. It is the correlation key across every token in the chain: ID Token, ID-JAGs, access tokens, and any downstream revocation signals.

actor identifies who is acting and on whose behalf. type=agent signals a machine exercising delegated human authority. actor.id is the agent instance; actor.sub is the user whose authority the agent is exercising. In a delegated OIDC flow, the outer sub and actor.sub reference the same user: sub is the authenticated principal, actor.sub makes the delegation beneficiary explicit so the OP can evaluate policy at every link in the chain.

The actor structure is doing similar work to the act claim defined in RFC 8693 Section 4.1 for representing the active actor in a delegation chain. A companion post on standardizing act across assertion grants and JWT access tokens explores how aligning these structures across ID Tokens, ID-JAGs, and JWT access tokens creates a consistent delegation model end-to-end.

constraints encode the mission lifecycle. expires_at is the deadline; the OP rejects expired requests and caps the issued session accordingly. delegation_depth limits how many additional hops the issued session may authorize, directly addressing scope amplification in multi-agent chains.

justification links this request to an external mandate record, providing the wire-format connection to the authoritative mission state owner when one exists.

display is what connects this to the consent model described in From Passports to Power of Attorney: the user sees the actual task, not scope strings, before approving. That alignment is a security primitive, not a UX improvement. OAuth scope strings (calendar:write contacts:read) are meaningful to developers and opaque to most users. Informed consent requires that the principal understands what they are authorizing, and scope strings today largely fail that bar. The ID Token with applied client_context is the cryptographic record that the user saw and approved this specific task description.

๐ŸŒ ID-JAG: The Cross-Domain Payoff

The value of encoding mission in the ID Token becomes concrete at the cross-domain boundary. In enterprise deployments, multiple resource servers often share a common Identity Provider (IdP) and rely on it as the source of identity assertions. An agent holding a mission ID Token can present it back to that IdP as the input to an Identity Assertion Authorization Grant (ID-JAG) exchange targeted at a Resource AS in that trust domain.

ID-JAG (draft-ietf-oauth-identity-assertion-authz-grant-02) combines RFC 8693 Token Exchange and RFC 7523 JSON Web Token (JWT) Bearer grants. The mission ID Token is the subject_token. The IdP issues a signed ID-JAG for the target Resource AS, which the agent presents using a JWT Bearer grant to obtain a scoped access token. In a production profile, both the mission ID Token and derived ID-JAGs should be sender-constrained to the client instance using DPoP (RFC 9449) or mutual TLS (mTLS, RFC 8705), so mission continuity does not also become replay continuity.

The IdP’s policy for each kind URI defines which audiences and resource patterns are permitted for that mission. An agent cannot exchange a meeting-scheduler mission for access to the payroll API. The IdP is the mandate enforcement point for every cross-domain exchange, not only initial authentication.

That enforcement is only as good as its policy configuration. Each kind URI must be registered in the OP’s client_context_values metadata for the requesting client, whether through Dynamic Client Registration (RFC 7591) or manual provisioning. That registration is where the permitted Resource AS audiences and resource URI patterns are bound to the purpose class. The agent declares intent at runtime; the OP enforces what was registered at provisioning time. The agent cannot expand its own scope by asserting a different kind at authentication time.

For deployments where policy complexity justifies it, the OP can externalize these decisions via OpenID AuthZen, a standardized Policy Enforcement Point (PEP) to Policy Decision Point (PDP) interface that allows the OP to call an external policy engine when evaluating a client_context request. This lets enterprise policy engines govern kind URI allow-lists, delegation depth limits, and step-up requirements through the same infrastructure used for other authorization decisions, without duplicating configuration at the OP.

With policy registered, the protocol runs in three phases.

Phase 1: Mission authentication. The agent authenticates with client_context. The authorization request should be submitted via Pushed Authorization Request (PAR, RFC 9126), keeping actor identity, delegation chains, and constraint details out of browser redirect URIs and server logs. A nonce in the request binds the ID Token to this specific flow, preventing replay across different authorization contexts. The OP issues a mission ID Token containing the approved purpose context.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "iss": "https://idp.example.com",
  "sub": "user-123",
  "aud": "calendar-agent-v2",
  "client_context": {
    "type": "purpose",
    "kind": "https://example.com/purposes/schedule-meeting",
    "id": "inv_a1b2c3d4",
    "actor": { "type": "agent", "id": "calendar-agent-v2", "sub": "user-123" },
    "constraints": { "expires_at": "2026-03-10T17:00:00Z", "delegation_depth": 1 }
  },
  "acr": "urn:mace:incommon:iap:silver",
  "exp": 1773075600
}

When the OP enforces step-up for a high-risk purpose.kind, the acr (Authentication Context Class Reference) claim records the achieved authentication assurance level. That acr carries into the ID-JAG, giving the Resource AS verifiable evidence not just that authentication happened but what assurance level it reached for this specific mission. This is something the authorization layer alone cannot produce.

The ID Token’s aud is the agent platform, not a resource server. The agent runtime is the first enforcement point in the chain, before any external call is made.

The agent harness receives the ID Token directly, validates that the approved client_context matches what was requested, checks constraints.expires_at before starting execution, enforces delegation_depth before dispatching sub-agents, and confirms max_uses has not been exceeded. This local enforcement at the orchestration layer is distinct from OP enforcement (at authentication time) and Resource AS enforcement (at access time). The agent harness sits between them and can act on the full approved purpose record before committing to any side effects, without requiring an access token or resource server call to do it.

Phase 2: Token Exchange for an ID-JAG. To reach tool APIs at a third-party Resource AS, the agent exchanges the mission ID Token at the IdP. A single exchange covers multiple APIs under the same Resource AS via the resource parameter (RFC 8707). For a different Resource AS, the agent repeats the exchange naming that AS as audience.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
POST /oauth2/token HTTP/1.1
Host: idp.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&requested_token_type=urn:ietf:params:oauth:token-type:id-jag
&audience=https://as.calendar.tools.example/
&resource=https://calendar-api.tools.example/
&resource=https://contacts-api.tools.example/
&scope=calendar:write+contacts:read
&subject_token=<mission_id_token>
&subject_token_type=urn:ietf:params:oauth:token-type:id_token

The issued ID-JAG carries client_context from the mission session. kind, id, actor, constraints, and justification carry through; display is stripped (UI only); params is stripped or redacted at the trust boundary. Only the minimum mission claims needed for downstream enforcement should cross a trust boundary. Subject identifiers may also need pairwise forms depending on the trust relationship between the IdP and the Resource AS.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
{
  "iss": "https://idp.example.com",
  "sub": "user-123",
  "aud": "https://as.calendar.tools.example/",
  "client_id": "calendar-agent-v2",
  "jti": "9e43f81b64a33f20116179",
  "iat": 1773075600,
  "exp": 1773075900,
  "resource": [
    "https://calendar-api.tools.example/",
    "https://contacts-api.tools.example/"
  ],
  "scope": "calendar:write contacts:read",
  "client_context": {
    "type": "purpose",
    "kind": "https://example.com/purposes/schedule-meeting",
    "id": "inv_a1b2c3d4",
    "actor": { "type": "agent", "id": "calendar-agent-v2", "sub": "user-123" },
    "constraints": { "expires_at": "2026-03-10T17:00:00Z", "delegation_depth": 1 }
  }
}

Phase 3: Access token at the Resource AS. The agent presents the ID-JAG using a JWT Bearer grant and receives a scoped access token. The Resource AS validates the ID-JAG signature against the IdP’s published JSON Web Key Set (JWKS), then can enforce constraints.expires_at, evaluate actor.type for delegation policy, and log client_context.id as the audit correlation key. The jti claim in the ID-JAG identifies this specific assertion; the Resource AS should track recently seen jti values within the token lifetime to prevent replay.

The id threads through every token in the chain. When the mandate terminates, the IdP stops issuing ID-JAGs against that id, and access tokens at Resource AS environments cannot be renewed. This requires the IdP to maintain revocation state for mission IDs: it becomes a stateful enforcement point, not purely a token issuer. The mandate service architecture described in Governing the Stay, Not Just the Entry is the natural place to hold and propagate that termination state. Already-issued access tokens still live until expiry or active invalidation, which is why short token lifetimes, introspection where appropriate, and event-driven revocation via Continuous Access Evaluation Protocol (CAEP) still matter.

โš–๏ธ RAR and OIDC Client Context: When to Use Each

OIDC client_context is not the only mechanism for mission-aware agents, and it is not always the right one. RAR is well-specified, widely supported, and maps cleanly to resource server policy. The FAPI 2.0 Security Profile (Financial-grade API) demonstrates this well: open banking and healthcare deployments using RAR in tightly governed single-AS contexts work precisely because the authorization server is a central authority for a defined resource domain. A deployment in a similar bounded context is probably better served by RAR than by adding client_context.

The cases where client_context and ID-JAG add the most value:

  • Multi-AS missions: the agent needs resource servers governed by different authorization servers. The OP-issued mission record becomes the portable trust anchor for downstream assertion issuance; RAR-scoped access tokens remain audience-bound.
  • Enterprise IdP trust: resource servers share a common IdP. ID-JAG is the natural access pattern. Encoding mission in the ID Token means the IdP enforces mission policy at every exchange.
  • User-delegated agents: the OP is where the user is present and authentication policy has authority. Encoding mission at authentication time means constraint enforcement, including session lifetime and refresh token bounds, happens before any token is issued.
  • Step-up enforcement: high-risk missions require stronger authentication. An AS receiving a RAR request cannot go back and require step-up. The OP can, but only if it knows what kind of mission is being authenticated.

The two mechanisms are complementary. A deployment using client_context at the OP to bound the session can also use RAR at individual ASes to shape the resulting access tokens. The gap is not that RAR is weak; it is that RAR does not by itself provide a standardized cross-AS continuity model for the same approved mission. The Execution Mandate from From Passports to Power of Attorney carries both: client_context.purpose at the authentication layer, authorization_details at the authorization layer. The same artifact at two different protocol layers.

GNAP (Grant Negotiation and Authorization Protocol, RFC 9635) addresses multi-RS delegation with a different architectural model: a centralized transaction server that negotiates access across multiple resource servers in a single interaction, without the shared-IdP assumption. It is worth acknowledging as an alternative for deployments where the enterprise IdP trust model does not apply.

๐Ÿงญ The Bootstrap Problem

The harder practical question is what it means to capture mission context when an agent deployment is first brought up.

RAR has a structural limitation for dynamic agents: to shape a token at a target AS, you need to know that AS at request time. For deterministic workflows, this is fine. For agents that discover tool requirements at runtime, every new tool call can require a new authorization flow with the user, unless there is some higher-level policy layer that can authorize later derivations.

client_context changes the shape of this problem. The mission is declared at authentication time against the IdP’s purpose policy. The IdP holds the mapping from kind URI to permitted audiences and resource patterns. When the agent needs to call a tool it did not know about at startup, it exchanges its existing mission ID Token at the IdP. The IdP evaluates whether the requested resource falls within the mission scope. No new user-facing authorization is required if and only if the newly requested audience and resource pattern fall inside the policy envelope the user already approved. If not, the exchange is denied or a new approval step is required.

The ID Token is the portable mission credential in the narrow sense that it can be brought back to the IdP and re-evaluated for downstream issuance. The mission is approved once, at authentication time, by the entity that authenticated the user. Every downstream token is a controlled derivation from that approval, bounded by the same constraints and by audience-specific policy at the exchange point.

The claim here is not that client_context solves agent security. It is that encoding mission in the ID Token, with IdP policy enforcement at every ID-JAG exchange, is a better fit for the cross-domain enterprise case than RAR alone. It moves the authentication bootstrap problem forward and creates the correlation key that revocation and audit need. The mandate service, continuous evaluation, and CAEP-based early termination described in Governing the Stay, Not Just the Entry are the layers built on top of this foundation.


If you work in identity, security architecture, or agent systems and are thinking about how to carry mission context through the authentication layer, I would be interested in how you are approaching this problem.

The draft specification is at github.com/mcguinness/connect-client-context.