| Internet-Draft | OAuth Chain Delegation | March 2026 |
| Liu, et al. | Expires 18 September 2026 | [Page] |
This specification defines the delegation_chain JWT claim for
OAuth 2.0 access tokens, enabling secure multi-hop delegation in
agent-to-agent scenarios. It provides a cryptographically verifiable
chain of delegation events, ensuring end-to-end auditability and
preventing privilege escalation across delegation hops.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 18 September 2026.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
In multi-agent systems, a primary agent (Agent A) may need to delegate a subset of its authorized operations to a secondary agent (Agent B). While RFC 8693 Token Exchange provides mechanisms for delegation, it does not define how to maintain a verifiable chain of delegation events across multiple hops.¶
This specification introduces the delegation_chain claim, which:¶
This specification complements the act claim defined in
[RFC8693] by providing detailed delegation history,
while act identifies the immediate actor.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals.¶
While this specification is optimized for use with Pushed Authorization Requests (PAR) [RFC9126], the mechanism defined herein is designed to be general-purpose and MAY be used with various OAuth 2.0 authorization flows, including but not limited to:¶
Different authorization flows may have different security considerations when using this specification. Implementations SHOULD carefully evaluate the security implications based on their specific deployment scenario.¶
The delegation_chain is an ordered array of delegation records,
from most recent to earliest. Each record represents one delegation hop.¶
{
"delegation_chain": [
{
"delegator_wit": "wit://agent-a.example",
"delegatee_wit": "wit://agent-b.example",
"delegation_timestamp": 1734516900,
"root_evidence_ref": "evidence-root-abc123",
"delegated_policy": {
"type": "rego",
"content": "package agent\ndefault allow = false\n\nallow {\n input.action == \"inventory_check\"\n input.item_id == \"123\"\n}",
"entry_point": "allow"
},
"operation_summary": "Delegate inventory check for item 123",
"delegator_signature": "eyJhbGciOiJFUzI1NiIs...",
"as_signature": "eyJhbGciOiJSUzI1NiIs..."
}
]
}
A delegation record transitions through three phases during its lifecycle:¶
This three-phase model ensures cryptographic binding between user authorization, agent identity, and policy constraints throughout the delegation chain.¶
| Field | Type | Requirement | Description |
|---|---|---|---|
| delegator_wit | string | REQUIRED | Workload Identity Token (WIT) of the delegating agent. |
| delegatee_sub | string | REQUIRED | Subject identifier of the receiving agent. |
| delegation_timestamp | NumericDate | REQUIRED | When this delegation was authorized. |
| delegated_policy | object | OPTIONAL | A subset of the delegator's authorized policy that is being delegated to the delegatee. This field SHOULD follow the structure defined in [I-D.ietf-oauth-rego-policy]. The delegated policy MUST be equal to or more restrictive than the delegator's policy. Policy permission expansion is NOT allowed. |
| operation_summary | string | OPTIONAL | Human-readable description of delegated operation. |
| root_evidence_ref | string | OPTIONAL |
Reference to the root authorization evidence record as defined
in the authorization evidence specification. This field MUST be
present for the first delegation (User → Agent A) and SHOULD be
propagated through the chain. The value SHOULD match the
evidence.id from the root token.
|
| delegator_signature | string | RECOMMENDED |
Cryptographic signature from the delegating agent's private key
over this delegation record. This provides dual-signature security
alongside as_signature, preventing malicious AS from
forging unauthorized delegations and ensuring non-repudiation.
|
| root_evidence_ref | string | OPTIONAL |
Reference to the root authorization evidence record as defined
in the authorization evidence specification. This field MUST be
present for the first delegation (User → Agent A) and SHOULD be
propagated through the chain. The value SHOULD match the
evidence.id from the root token.
|
| delegator_signature | string | RECOMMENDED |
Cryptographic signature from the delegating agent's private key
over this delegation record. This provides dual-signature security
alongside as_signature, preventing malicious AS from
forging unauthorized delegations and ensuring non-repudiation.
|
| as_signature | string | REQUIRED | AS signature over this delegation record. |
Both as_signature and delegator_signature MUST be
computed over the same set of fields using JSON Canonicalization Scheme
(JCS) as defined in RFC 8785:¶
delegator_wit¶
delegatee_sub¶
delegation_timestamp¶
delegated_policy (if present)¶
operation_summary (if present)¶
root_evidence_ref (if present)¶
Both signature fields MUST be excluded from their respective signature computations. The signatures MUST use detached JWS format with appropriate algorithm identifiers.¶
The delegator_signature uses the delegating agent's WIT-bound
private key, while as_signature uses the Authorization Server's
signing key. This dual-signature approach ensures:¶
The array MUST be ordered from most recent delegation to earliest:¶
This ordering allows efficient validation starting from the immediate delegator.¶
+--------+ +---------+ +--------+ +---------+
| User | | Agent A | | AS | | Agent B |
+--------+ +---------+ +--------+ +---------+
| | | |
| (1) Authorize | | |
|---------------->| | |
| | | |
| | (2) PAR | |
| |---------------->| |
| | | |
| (3) Consent | | |
|<----------------|---------------->| |
| | | |
| | (4) Token A | |
| | (no chain) | |
| |<----------------| |
| | | |
| | (5) Delegation Request (PAR) |
| |----------------------------------> |
| | - subject_token (Token A) |
| | - delegatee_wit (Agent B) |
| | - delegated_policy (optional) |
| | | |
| | | (6) Validate |
| | | - Token A valid |
| | | - Scope subset |
| | | - Agent B auth |
| | | |
| | | (7) Issue Token B
| | |---------------->|
| | | with delegation_chain
| | | |
| | | | (8) API Request
| | | |------------>
| | | |
| | | | (9) Validate chain
| | | |<------------
Agent A obtains authorization from the user through a standard OAuth flow. The resulting token does not contain a delegation_chain (it is the root authorization).¶
Agent A initiates delegation using Pushed Authorization Request (PAR) with JWT Secured Authorization Request [RFC9126]. The delegation parameters are encoded in a JWT, providing integrity protection and supporting large payloads.¶
POST /par HTTP/1.1 Host: as.example.com Content-Type: application/oauth-authz-req+jwt OAuth-Client-Attestation: wit://agent-b.example OAuth-Client-Attestation-PoP: <PoP-Signature> eyJhbGciOiJFUzI1NiIsImtpZCI6ImFnZW50LWEta2V5In0.ewogICJpc3MiOiAid2l0Oi8vdHBtMi5udmlkaWEuc2hhMjU2LmFiYzEyMy4uLiIsCiAgImF1ZCI6ICJodHRwczovL2FzLmV4YW1wbGUuY29tIiwKICAicmVzcG9uc2VfdHlwZSI6ICJjb2RlIiwKICAiZ3JhbnRfdHlwZSI6ICJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6Z3JhbnQtdHlwZTp0b2tlbi1leGNoYW5nZSIsCiAgInN1YmplY3RfdG9rZW4iOiAiPGFnZW50LWEtdG9rZW4-IiwKICAic3ViamVjdF90b2tlbl90eXBlIjogInVybjppZXRmOnBhcmFtczpvYXV0aDp0b2tlbi10eXBlOmFjY2Vzc190b2tlbiIsCiAgInJlcXVlc3RlZF90b2tlbl90eXBlIjogInVybjppZXRmOnBhcmFtczpvYXV0aDp0b2tlbi10eXBlOmFjY2Vzc190b2tlbiIsCiAgInNjb3BlIjogImNhcnQ6cmVhZCIsCiAgImRlbGVnYXRpb25fcmVhc29uIjogIkludmVudG9yeSBjaGVjayBmb3IgaXRlbSBYIiwKICAiZGVsZWdhdGVlX3dpdCI6ICJ3aXQ6Ly9zZ3guaW50ZWwuc2hhMjU2LmRlZjQ1Ni4uLiIKfQ.signature
The JWT payload (decoded) contains:¶
{
"iss": "wit://agent-a.example",
"aud": "https://as.example.com",
"response_type": "code",
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token": "<agent-a-token>",
"subject_token_type": "urn:ietf:params:oauth:token-type:access_token",
"requested_token_type": "urn:ietf:params:oauth:token-type:access_token",
"delegated_policy": {
"type": "rego",
"content": "package agent\ndefault allow = false\n\nallow {\n input.action == \"inventory_check\"\n input.item_id == \"123\"\n}",
"entry_point": "allow"
}
"delegatee_wit": "wit://agent-b.example"
}
The AS MUST perform the following validations:¶
Upon successful validation, the AS issues a new token for Agent B with:¶
When the AS issues a delegated token, it extends the delegation_chain:¶
If Agent A's token has no delegation_chain (root authorization), the AS creates a new chain with one entry:¶
{
"delegation_chain": [
{
"delegator_wit": "wit://agent-a.example",
"delegatee_wit": "wit://agent-b.example",
"delegation_timestamp": 1734516900,
"root_evidence_ref": "evidence-root-abc123",
"delegated_policy": {
"type": "rego",
"content": "package agent\ndefault allow = false\n\nallow {\n input.action == \"cart_op\"\n}",
"entry_point": "allow"
},
"delegator_signature": "eyJhbGciOiJFUzI1NiIs...",
"operation_summary": "Delegate cart operations",
"delegator_signature": "eyJhbGciOiJFUzI1NiIs...",
"operation_summary": "Delegate cart operations",
"delegator_signature": "eyJhbGciOiJFUzI1NiIs...",
"operation_summary": "Delegate cart operations",
"delegator_signature": "eyJhbGciOiJFUzI1NiIs...",
"operation_summary": "Delegate cart operations",
"as_signature": "eyJhbGciOiJSUzI1NiIs..."
}
]
}
If Agent B further delegates to Agent C, the AS prepends a new record:¶
{
"delegation_chain": [
{
"delegator_wit": "wit://agent-b.example",
"delegatee_wit": "wit://agent-c.example",
"delegation_timestamp": 1734517800,
"root_evidence_ref": "evidence-root-abc123",
"delegated_policy": {
"type": "rego",
"content": "package agent\ndefault allow = false\n\nallow {\n input.action == \"inventory_check\"\n input.item_id == \"123\"\n}",
"entry_point": "allow"
},
"delegator_signature": "eyJhbGciOiJFUzI1NiIs...",
"operation_summary": "Check inventory for item 123",
"delegator_signature": "eyJhbGciOiJFUzI1NiIs...",
"operation_summary": "Check inventory for item 123",
"as_signature": "eyJhbGciOiJSUzI1NiIs..."
},
{
"delegator_wit": "wit://agent-a.example",
"delegatee_wit": "wit://agent-b.example",
"delegation_timestamp": 1734516900,
"root_evidence_ref": "evidence-root-abc123",
"delegated_policy": {
"type": "rego",
"content": "package agent\ndefault allow = false\n\nallow {\n input.action == \"cart_op\"\n}",
"entry_point": "allow"
},
"as_signature": "eyJhbGciOiJSUzI1NiIs..."
}
]
}
Resource Servers validate the delegation_chain as follows:¶
For each record in the chain, verify the as_signature using
the AS's public key. This ensures:¶
The last record in the chain (highest index) represents the earliest
delegation. Its delegator_wit identifies the agent that holds
the root authorization (the token directly authorized by the user).¶
The Resource Server MUST verify that:¶
delegation_chain claim);¶
evidence and
audit_trail claims as defined in
the authorization evidence draft, providing
cryptographic proof of user consent.¶
If the RS cannot verify the root authorization anchor, it MUST reject the request.¶
For each delegation record, the Resource Server SHOULD verify both:¶
The dual-signature mechanism prevents:¶
The Resource Server SHOULD verify the current status of all Workload Identity Tokens (WITs) referenced in the delegation_chain by:¶
If any WIT in the chain has been revoked or expired, the entire delegation_chain MUST be considered invalid, even if individual records remain cryptographically valid.¶
Verify the chain is continuous:¶
Verify scope constraints are progressively narrowing:¶
The AS MUST ensure that delegation cannot expand privileges:¶
The as_signature on each record ensures chain integrity.
Agents MUST NOT be able to:¶
This enables:¶
Implementations SHOULD enforce maximum delegation depth to:¶
A RECOMMENDED default maximum depth is 5 hops.¶
This specification registers the following claim in the "JSON Web Token Claims" registry:¶
The following shows a token held by Agent C after two delegation hops (User → Agent A → Agent B → Agent C):¶
{
"iss": "https://as.example.com",
"sub": "user_12345",
"aud": "https://api.shop.example",
"exp": 1734520500,
"iat": 1734517800,
"act": {
"sub": "wit://agent-c.example"
},
"delegation_chain": [
{
"delegator_wit": "wit://agent-b.example",
"delegatee_wit": "wit://agent-c.example",
"delegation_timestamp": 1734517800,
"root_evidence_ref": "evidence-root",
"delegated_policy": {
"type": "rego",
"content": "package agent\ndefault allow = false\n\nallow {\n input.action == \"inventory_check\"\n input.item_id == \"123\"\n}",
"entry_point": "allow"
},
"operation_summary": "Check stock for item 123",
"delegator_signature": "eyJhbGciOiJFUzI1NiIs...sig2-delegator",
"as_signature": "eyJhbGciOiJSUzI1NiIs...sig2"
},
{
"delegator_wit": "wit://agent-a.example",
"delegatee_wit": "wit://agent-b.example",
"delegation_timestamp": 1734516900,
"root_evidence_ref": "evidence-root",
"delegated_policy": {
"type": "rego",
"content": "package agent\ndefault allow = false\n\nallow {\n input.action == \"cart_op\"\n}\nallow {\n input.action == \"inventory_op\"\n}",
"entry_point": "allow"
},
"operation_summary": "Delegate inventory operations",
"delegator_signature": "eyJhbGciOiJFUzI1NiIs...sig1-delegator",
"as_signature": "eyJhbGciOiJSUzI1NiIs...sig1"
}
],
"evidence": {
"id": "evidence-root",
"user_confirmation": {
"displayed_content": "Allow shopping assistant to manage cart",
"user_action": "confirmed_via_button_click",
"timestamp": 1734516000,
"interface_version": "consent-ui-v2.1"
},
"session_context": {
"session_id": "session_xyz789",
"channel": "mobile-app"
},
"as_signature": "eyJhbGciOiJSUzI1NiIs...sig0"
},
"audit_trail": {
"evidence_ref": "evidence-root",
"semantic_expansion_level": "medium",
"interpretation_notes": "User said 'manage cart', delegated to inventory operations"
}
}
This token shows:¶
Each delegation hop MUST be represented as a separate JWT access token. The delegating agent signs each token using its private key, creating a cryptographically verifiable chain of custody.¶
For each delegation from Agent-X to Agent-Y:¶
Resource Servers MUST validate:¶
This per-hop signing approach provides:¶
While [I-D.ietf-oauth-identity-chaining] focuses on static identity federation across organizational boundaries, this specification targets dynamic AI agent delegation with the following distinctions:¶
This makes the mechanism suitable for AI-to-AI scenarios where agents need temporary, auditable delegation without pre-established trust relationships. Implementations MAY use WIT with attestation evidence for enhanced security, but it is not required by this specification.¶