| Internet-Draft | Identity Continuation Assertion | August 2026 |
| McGuinness | Expires 27 February 2027 | [Page] |
This document defines the Identity Continuation Assertion, a short-lived, sender-constrained JWT used as an OAuth 2.0 Token Exchange subject token. It lets an IdP Authorization Server (IdP) issue an onward Identity Assertion JWT Authorization Grant (ID-JAG) when a user's request crosses service boundaries after the user is no longer present. The profile targets deployments in which several Resource Authorization Servers trust one IdP and use audience-local subject identifiers that only the IdP can resolve. It complements offline attenuation for intra-domain fan-out that does not change the subject.¶
This note is to be removed before publishing as an RFC.¶
The latest revision of this draft can be found at https://mcguinness.github.io/draft-mcguinness-oauth-id-continuation-assertion/draft-mcguinness-oauth-id-continuation-assertion.html. Status information for this document may be found at https://datatracker.ietf.org/doc/draft-mcguinness-oauth-id-continuation-assertion/.¶
Discussion of this document takes place on the Web Authorization Protocol Working Group mailing list (mailto:oauth@ietf.org), which is archived at https://mailarchive.ietf.org/arch/browse/oauth/. Subscribe at https://www.ietf.org/mailman/listinfo/oauth/.¶
Source for this draft and an issue tracker can be found at https://github.com/mcguinness/draft-mcguinness-oauth-id-continuation-assertion.¶
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 27 February 2027.¶
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.¶
OAuth 2.0 [RFC6749] issues access tokens for use at protected resources, and OAuth 2.0 Token Exchange [RFC8693] trades one token for another when a request crosses a trust boundary. The Identity Assertion JWT Authorization Grant (ID-JAG) [I-D.ietf-oauth-identity-assertion-authz-grant] applies Token Exchange to identity: an IdP Authorization Server (IdP) mints a grant that names the user for a single downstream audience. Each such exchange assumes the subject's credential (an ID Token, refresh token, or SAML assertion) is still available when the grant is minted.¶
In practice, many requests outlive that moment. An authenticated request may cross several services after the user is no longer present, or reach an audience the original credential never addressed. The first hop can still present the subject's credential to obtain an ID-JAG, but a workload further along the chain holds none of these credentials. This is hardest when each Resource Authorization Server names the user by a pairwise, audience-local subject that only the IdP can resolve, and that differs at every server: the later workload has no way to name the user for the next audience. Only the IdP can perform that mapping, so each continuation is a fresh mint from the IdP rather than a reused or offline-attenuated token.¶
This document defines the Identity Continuation Assertion: a short-lived,
sender-constrained JWT that a later workload presents as the subject_token
of a Token Exchange request to obtain the next audience-scoped ID-JAG, with no
further user interaction. The assertion carries a continuation handle that
ties the request to the authorization state the IdP recorded when the chain
was established. A Resource Authorization Server (RAS) still trusts only the
IdP to name the user and scope authority; at each hop the IdP resolves
identity and re-checks the requested authority against the root-chain
envelope. Continuation therefore stays a fresh policy decision at every
boundary, never a bearer of standing authority.¶
Each trust domain the chain continues from has two roles: a RAS that accepts an ID-JAG and binds the hop, and a Continuation Assertion Issuer (CAI) that mints the Identity Continuation Assertion the workload presents to the IdP. A trusted intra-domain carrier surfaces the accepted hop's handle to workloads inside the domain (Section 6.6); the carrier is a deployment choice, not a role of its own, and one party may run all of these within a domain (Section 9.7).¶
The profile stays deliberately narrow: it defines no new access-token format, a Resource Server never consumes the Identity Continuation Assertion directly, and a CAI never names the user for the target audience.¶
Use this profile when a boundary re-mints the user's identity, that is:¶
the next audience uses a pairwise subject only the IdP can resolve;¶
the target trusts the IdP, not the previous issuer, to name the user; and¶
current revocation and policy must be rechecked at every boundary.¶
Use offline attenuation, such as [I-D.li-oauth-delegated-authorization], when the subject and the trusted issuer both stay stable across the boundary and offline delegation semantics are acceptable, for example intra-domain fan-out under one workload identity. The two compose: offline attenuation inside a trust domain, continuation where a boundary re-mints the subject.¶
This document profiles Token Exchange [RFC8693], JWT [RFC7519], ID-JAG [I-D.ietf-oauth-identity-assertion-authz-grant], and OAuth Identity Chaining [I-D.ietf-oauth-identity-chaining]. It adds:¶
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, as shown here.¶
This document uses the following terms:¶
The authority that authenticates the user, maps the user to each audience-local subject, and issues onward grants.¶
An Authorization Server that protects a particular API and trusts the IdP for subject resolution. It exchanges an ID-JAG for an API access token. [I-D.ietf-oauth-identity-assertion-authz-grant] abbreviates this role (AS).¶
The server hosting the protected API. It never consumes an Identity Continuation Assertion or uses a continuation handle for authorization.¶
An Identity Assertion JWT Authorization Grant [I-D.ietf-oauth-identity-assertion-authz-grant] issued for a target RAS.¶
Describes any of: an ID-JAG that carries the identity_continuation_handle
claim (Section 4); a governing authorization that permits continuation;
or the root exchange such an authorization governs (Section 6.3.2).¶
A short-lived, sender-constrained JWT from a CAI, presented to
the IdP as a Token Exchange subject_token to obtain an onward ID-JAG.¶
An IdP-held tree of hops under one governing authorization (Section 5).¶
The role trusted by the IdP to issue Identity Continuation Assertions for a tenant. It may be a RAS, gateway, or dedicated service, but never resolves the target audience's user subject.¶
The workload presenting the assertion to the IdP, named by act and
authenticated by actor_token.¶
The actor at the root of a chain: the authenticated OAuth client that
obtains the first ID-JAG (Section 6.3.3); this is the Client of
[I-D.ietf-oauth-identity-assertion-authz-grant] at the root exchange.
Unlike a current actor, it need not present an actor_token.¶
The administrative boundary within which the chain and CAI trust are configured; its determination is deployment-defined (Section 9.7).¶
An administrative and authentication boundary within which workloads can be directly authenticated, comparable to WIMSE [I-D.ietf-wimse-arch]. Its identifier is deployment-defined.¶
The server-derived, deployment-specific mechanism that surfaces an accepted hop's handle to authorized workloads within a trust domain (Section 6.6).¶
identity_continuation_handle):An opaque, unguessable, IdP-generated reference to one hop of a continuation chain; see Section 4.¶
A root or continuation record; a continuation record carries an immutable parent reference and a root record has none. Its lineage is its path to the root.¶
The server-side consent and policy record, resolved from the root subject token, that anchors a chain and bounds every continuation under it (Section 5).¶
The state the IdP records at establishment and evaluates every continuation against, anchored to the chain's governing authorization (Section 5); its dimensions and bounds are defined in Section 6.3.2.¶
The subject identifier under which a particular RAS names the user. Distinct Resource Authorization Servers may name the same user with different identifiers; only the IdP holds the map between them.¶
Client-side attenuated delegation, in which a party narrows and forwards a credential without contacting the IdP; contrast the IdP-minted continuation this profile defines (Section 1.1).¶
The Identity Continuation Assertion has token type
urn:ietf:params:oauth:token-type:identity-continuation and media type
application/oauth-identity-continuation+jwt (Section 11). It is a JWT
[RFC7519] in JWS Compact Serialization. The CAI MUST set the JOSE typ
header to oauth-identity-continuation+jwt and MUST sign with an asymmetric
algorithm the IdP accepts (Section 9.9). The assertion MUST NOT be
encrypted (JWE) or use nested signing.¶
The following is a non-normative example of the Identity Continuation Assertion claim set:¶
{
"iss": "https://cai.expenses.example/",
"aud": "https://idp.example/",
"identity_continuation_handle": "kW4uJ8pTe2NxA6rQvD1zYs",
"act": {
"iss": "https://expenses.example/",
"sub": "expense-service"
},
"cnf": {
"jkt": "base64url-current-actor-key-thumbprint"
},
"iat": 1710000500,
"exp": 1710000620,
"jti": "k7Qm2Xp9Rf4sLc3vBw8aZ1"
}
¶
The claims have the following meanings and requirements:¶
iss:REQUIRED. The CAI that issued the assertion; the IdP verifies its issuer trust per Section 6.3.4, rule 3, and its signature per rule 2.¶
aud:REQUIRED. A single string exactly matching the IdP issuer identifier, not its token endpoint URL.¶
identity_continuation_handle:act:REQUIRED. The current actor presenting the Token Exchange request, encoded
as a single-level act claim per [RFC8693], with a REQUIRED iss and
sub, both non-empty strings. Additional members MAY carry further identity
attributes but are non-authoritative and MUST NOT affect identity,
authorization, lineage, or issuance; a recipient MUST ignore members it does
not understand, and exp, nbf, aud, scope, cnf, and nested act
MUST NOT be present.¶
cnf:REQUIRED. A confirmation claim [RFC7800] that binds the assertion to the
presenting actor's key. It MUST contain exactly one method: jkt, the JWK
SHA-256 thumbprint [RFC7638] of the DPoP key [RFC9449].¶
iat, exp:REQUIRED. exp MUST follow iat, and exp - iat MUST NOT exceed 300
seconds.¶
jti:REQUIRED. A replay-detection identifier that MUST be unique per iss
during the assertion validity window and MUST contain at least 128 bits
of entropy.¶
The assertion is a subject token whose subject the IdP resolves from the
referenced hop, not an [RFC7523] JWT-profile assertion. It MUST NOT contain
a top-level sub, auth_time, acr, amr, or sid claim (these come from
the root-chain envelope), nor the Token Exchange request parameters
audience, resource, scope,
authorization_details, or requested_token_type (these are supplied by
the request). The assertion's aud identifies the IdP, not the requested
target.¶
Other top-level claims MAY appear but MUST be ignored for validation, authorization, and issuance.¶
identity_continuation_handle)
An identity_continuation_handle is an opaque, non-bearer reference to one
IdP-held hop of a chain. The IdP mints a fresh handle for each hop and carries
it in that hop's ID-JAG; continuing from a hop produces a child hop with its
own handle, recorded against the hop it continued from.¶
The following rules apply:¶
When it establishes or continues a chain (Section 6.3.2), the IdP
MUST embed a fresh identity_continuation_handle claim in the issued
ID-JAG, for that root or child hop, and MUST NOT reuse a handle across hops.
An ID-JAG carrying this claim is continuation-capable.¶
identity_continuation_handle MUST contain at least 128 bits of entropy,
MUST NOT contain user-identifying information, and MUST consist of 22 to
256 characters drawn from the base64url alphabet (A-Z, a-z,
0-9, -, _).¶
The handle crosses a trust boundary only inside an ID-JAG (to the RAS) or an Identity Continuation Assertion (to the IdP), never standalone, and MUST NOT appear in an access token or external Resource Server authorization claim. Authorized workloads MAY observe it only as intra-domain context (Section 6.6).¶
A continuation-aware RAS binds the handle to the authorization state it establishes (Section 6.4); RASes, Resource Servers, and CAIs MUST NOT modify the value. A hop is continuable only after that acceptance and binding (Section 6.5); the IdP MUST use the handle only to resolve hop state, subject, and policy, never as authority.¶
A hop's parent reference is immutable. The IdP MUST derive lineage only by walking parent references to the root; it does not maintain a single chain-wide actor history, so concurrent sibling continuations are independent branches.¶
A chain is continuable only while active at the IdP. Each cross-boundary hop is a fresh policy check. Revoking a hop stops its subtree at the next continuation, fail-closed, but does not invalidate already-issued ID-JAGs or access tokens; the revocation window is therefore bounded by their respective lifetimes.¶
This is the deliberate difference from an offline-attenuated token, whose minted child stays usable for its lifetime without contacting an authority.¶
Three independent lifetimes govern a continuation: the ID-JAG's short redemption window; the access-token lifetime the accepting RAS sets, which this profile does not constrain (Section 6.4); and the IdP-held continuation chain. Revoking the chain does not shorten an already-issued access token, and an access token outliving the chain does not extend it.¶
ID-JAG redeem |==| access token |===========| RAS-set, independent IdP-held chain |=========================| IdP-held, spans hops¶
The governing authorization (Section 2) anchors to a lifecycle token: a refresh
token anchors to its OAuth grant, and sid or SessionIndex anchors to its
session. Rotation of a refresh token does not affect the grant anchor.¶
A chain ends when:¶
the grant it is anchored to expires or is revoked;¶
the session it is anchored to terminates; or¶
continuation consent or policy for it is withdrawn.¶
A session-anchored chain MUST NOT outlive its session; only grant-anchored chains may outlive logout. Ending a chain this way bounds only new continuations; an ID-JAG already issued remains redeemable for its own lifetime, since redemption is not a continuation.¶
The IdP MUST bound chain lifetime by the governing authorization. It MUST support administrative revocation of an entire chain and MAY revoke an individual hop's subtree, and MUST reject continuation on a revoked, expired, or ended chain.¶
How an IdP surfaces chains to users and administrators for review and revocation is deployment-specific; [GRANT-MGMT] describes OAuth grant management for that purpose.¶
A continuation reuses the Token Exchange loop once per boundary: the root exchange mints the first ID-JAG, and each later boundary mints the next from an Identity Continuation Assertion. Handles H0 and H1 below name the successive hops (Section 4).¶
Client Workload CAI IdP RAS | | | | | | base ID-JAG exchange | | | |---------------------------------->| | | issue ID-JAG (H0) | | | |<----------------------------------| | | present ID-JAG | | | |---------------------------------------------->| | issue access token, bind H0 | | |<----------------------------------------------| | | | | | | carrier surfaces H0 to a later workload | | | | | | | | request assertion | | | |---------->| | | | | attest hop, actor, key| | | |<----------| | | | | present assertion | | | |---------------------->| | | | issue ID-JAG (H1) | | | |<----------------------| |¶
H1 then travels to the next boundary exactly as H0 did, and the loop repeats until a hop reaches a terminal RAS. A hop is PENDING when the IdP issues its ID-JAG, ACCEPTED once the RAS binds it, and CONTINUABLE while a mapped CAI attests the still-active binding (Section 6.5). The workload that continues is a later party, not the original client that established the chain.¶
Each role validates only within its authority, and no artifact or role alone authorizes continuation (Section 9.7). The sections that follow trace artifact production and processing: the CAI issues the assertion (Section 6.2), the IdP validates the exchange and issues the next ID-JAG (Section 6.3), the accepting RAS binds and processes it (Section 6.4), and the domain surfaces the handle to later continuers (Section 6.6).¶
The CAI mints the Identity Continuation Assertion a workload presents to
continue a chain across a boundary. It MUST issue only for an actor in the
attested RAS's trust domain; actor authentication and the issuance protocol
are deployment-specific. The CAI MUST set the assertion's aud to the IdP
recorded in the hop's RAS binding (Section 6.4); it MUST NOT accept an
IdP audience supplied by the requester.¶
The current actor is a control-plane participant, not a bare-handle transporter: it presents the handle read from its own intra-domain context, with its key and any narrowing hints, to its CAI. The handle is advisory input, re-verified against RAS-bound state (Section 6.5) by the checks below before any assertion issues.¶
The CAI MUST authenticate the actor and issue only after establishing that:¶
the handle came through an authenticated, confidential, integrity-protected chain path or equivalent authenticated state;¶
the current actor is authorized under CAI policy to continue the chain;¶
the current actor controls the key placed in cnf;¶
act names that actor and, if offline attenuation reached the actor, its
delegation artifact is valid;¶
the actor is bound to the current transaction and the handle matches that transaction's RAS-bound state; and¶
a recheck against authoritative RAS state confirms the authorization remains active and continuation remains permitted.¶
Possession of a handle or carrier token alone is insufficient. Target or purpose hints can narrow CAI issuance but MUST NOT control the IdP's target decision, and propagated context MUST NOT override the root-chain envelope.¶
An Identity Continuation Assertion is used as the subject_token of an OAuth
2.0 Token Exchange request [RFC8693]. The root exchange and a continuation
exchange use the same Token Exchange framework: a continuation exchange
substitutes an Identity Continuation Assertion for the root credential and
additionally supplies the actor authentication and DPoP proof described below.
The IdP establishes the chain; no request parameter asks it to do so
(Section 6.3.2).¶
The root exchange presents a normal subject token, such as an ID Token, refresh token, or SAML assertion:¶
POST /token HTTP/1.1 Host: idp.example Content-Type: application/x-www-form-urlencoded DPoP: <proof of possession of the cnf key> grant_type=urn:ietf:params:oauth:grant-type:token-exchange &requested_token_type=urn:ietf:params:oauth:token-type:id-jag &audience=https://ras.travel.example/ &resource=https://api.travel.example/ &scope=trips.read &subject_token=<id_token | refresh_token | SAML assertion> &subject_token_type=<normal-subject-token-type> &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion=<JWT>¶
On the root exchange, actor_token is OPTIONAL (Section 6.3.2). The
root exchange and its ID-JAG conform to the base ID-JAG profile
([I-D.ietf-oauth-identity-assertion-authz-grant]) except where this document
extends it for continuation-capable issuance.¶
A continuation exchange presents an Identity Continuation Assertion and adds
the actor_token and a DPoP proof of the cnf key:¶
POST /token HTTP/1.1 Host: idp.example Content-Type: application/x-www-form-urlencoded DPoP: <proof of possession of the cnf key> grant_type=urn:ietf:params:oauth:grant-type:token-exchange &requested_token_type=urn:ietf:params:oauth:token-type:id-jag &audience=https://ras.travel.example/ &resource=https://api.travel.example/ &scope=trips.read &subject_token=<identity-continuation-assertion> &subject_token_type=urn:ietf:params:oauth:token-type:identity-continuation &actor_token=<sender-constrained-current-actor-credential> &actor_token_type=<actor-token-type> &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion=<JWT>¶
The requested audience, resource, scope, requested_token_type, and
any authorization_details [RFC9396] are supplied by the Token Exchange
request and never by the assertion (Section 3.2). A request can carry
multiple resource indicators [RFC8707], which the IdP treats as an
order-independent set; the envelope-containment check (Section 6.3.4, rule 7)
applies to authorization_details and to scope alike. Client authentication
is required on every exchange (Section 6.3.3).¶
The IdP, not the client, establishes a chain, and MUST do so when the
governing authorization for a root exchange is continuation-capable;
advertised support (Section 7) signals capability, not authority. To
establish, the IdP MUST include the root handle in the ID-JAG. The root
exchange MUST include a valid DPoP proof [RFC9449], which the IdP MUST bind
to the ID-JAG in cnf. Absent the continuation authorization or a valid
proof, the IdP MUST NOT establish a chain or include an
identity_continuation_handle.¶
The root subject token MUST resolve to one of these lifecycle anchors:¶
a refresh token's OAuth grant;¶
an ID Token sid [OIDC.FrontChannelLogout] resolving to an active IdP
session for that user and client; or¶
a SAML SessionIndex [SAML2.Core] resolving to an active IdP session
for that user and client.¶
The IdP MUST NOT root a chain from an unresolved anchor or an access token;
non-user-rooted authority is out of scope. sid and SessionIndex are used
only for resolution and MUST NOT enter assertions or chain context.¶
Server-side consent and policy make the governing authorization continuation-capable and populate the root-chain envelope from authentication, consent, and tenant policy:¶
the authenticated user and authentication context (auth_time, acr,
amr);¶
the authorization basis for onward targets;¶
the continuation authorization: the actors or trust domains permitted to continue the chain, and the basis on which that permission was established;¶
any maximum actor-chain depth set by policy; and¶
the governing authorization (Section 5) and the chain's expiry.¶
Token claims cannot supply these values. Every dimension is an establishment-time ceiling that later policy MAY narrow or revoke but MUST NOT broaden; broadening requires a new chain, and consent granted afterward does not widen the envelope.¶
The envelope takes one of two forms:¶
an enumerated-target envelope, listing the permitted audiences and, where used, their resources, scopes, and authorization details [RFC9396]; or¶
a policy-basis envelope, a stable authorization basis against which the IdP evaluates each requested target at request time.¶
Either form is fixed at establishment, not whatever the user could later authorize.¶
The root actor is the authenticated OAuth client; its identity rests entirely
on the mapping in Section 6.3.3. Base ID-JAG's recommendation to use a
confidential client therefore applies to a continuation-capable root. An
optional actor_token MUST be valid, accepted for continuation, and
sender-constrained to the confirmed key, and MUST
identify that client and designate the IdP where applicable; the IdP records
the root actor and key only after this validation.¶
For every root or child hop, the IdP records the target RAS and the CAIs mapped to it; the mapping may be static tenant configuration, and only a mapped CAI may attest that hop.¶
Establishment is at-least-once: retrying a lost response MAY create a second chain. Revocation of the governing authorization applies to every chain rooted in it, and the actor-chain depth bound is enforced per branch. A retried establishment MUST NOT evade the fan-out, rate, or hop-count limits configured for the governing authorization (Section 8).¶
The client-to-actor mapping below applies to every exchange; the act and
actor_token matching and the DPoP proof apply to a continuation exchange, and
the root exchange's DPoP requirement is specified in Section 6.3.2.¶
The current actor MUST authenticate as an OAuth client. Four rules govern how that authentication maps to an actor identity:¶
Authoritative mapping. The IdP MUST map the authenticated client to an actor identity; self-asserted mappings MUST NOT be accepted.¶
Root versus continuation. On a root exchange, client authentication alone
identifies the root actor (Section 6.3.2). On a continuation
exchange, the IdP MUST also match that identity to the assertion's act and
the actor_token.¶
Dual-use JWT. A sender-constrained JWT MAY serve as both client assertion
and actor_token when it satisfies both profiles; for [RFC7523] its sub
is the client_id and the IdP MUST authorize its issuer for that client.
Otherwise the client authenticates separately.¶
Target registration. The onward ID-JAG client_id is the current actor's
identifier at the target RAS, so the actor needs a registration or resolvable
client identity at each target.¶
The actor_token MUST NOT be bearer: for a JWT the IdP verifies cnf.jkt,
and for an opaque token it obtains equivalent confirmation from authoritative
metadata such as introspection [RFC7662]; its issuer, acceptance, sender
constraint, and applicability are checked by Section 6.3.4 rule 5. The IdP
MUST compare the actor iss and sub as case-sensitive strings with no
transformation or canonicalization ([RFC7519]), across actor_token, act,
and the authenticated client, and identities in different tenants never
compare equal.¶
The actor MUST present a DPoP proof [RFC9449] for the key in cnf.jkt.
DPoP is the single mandatory confirmation method, so the target validates
confirmation identically to a directly issued ID-JAG, and this version defines
no mutual-TLS variant [RFC8705] (Appendix C). The onward ID-JAG MUST use
the same DPoP key; key rotation takes effect when the actor obtains a new
assertion and actor token bound to the new key.¶
Four signals bind the continuation request to the current actor. The identities and key possession they establish must be mutually consistent:¶
| Signal | What it establishes |
|---|---|
| Client authentication | who is calling the IdP token endpoint |
actor_token
|
the actor vouched for by its workload-identity issuer |
Assertion act
|
the actor the CAI bound to the accepted hop |
| DPoP | live possession of the key binding all three to this request |
For a continuation exchange, the IdP MUST reject the request unless every rule below holds; their order is not significant, though one rule's input may come from another's resolution.¶
Request parameters.¶
exactly one each of grant_type, subject_token, subject_token_type,
requested_token_type, actor_token, actor_token_type, and
audience;¶
zero or more resource, and at most one each of scope and
authorization_details, all OPTIONAL and, when present, evaluated by
rule 7; and¶
grant_type is urn:ietf:params:oauth:grant-type:token-exchange,
subject_token_type is
urn:ietf:params:oauth:token-type:identity-continuation, and
requested_token_type is urn:ietf:params:oauth:token-type:id-jag;¶
Assertion well-formedness.¶
the assertion is a JWT whose JOSE typ header is
oauth-identity-continuation+jwt;¶
it contains exactly one value for each claim required by Section 3.2 and none of the claims that section forbids;¶
iss, aud, identity_continuation_handle, and jti are non-empty
strings, act and cnf are JSON objects with cnf naming exactly one
confirmation method, and iat and exp are NumericDate numbers;¶
the signature validates under an acceptable algorithm (Section 9.9, [RFC8725]) with the issuer's resolved signing keys (Section 7); and¶
aud exactly matches the IdP's issuer identifier;¶
Issuer trust. The assertion iss is trusted for the tenant, mapped to
the hop's accepting RAS, and authorized to pair with the actor_token
issuer for that tenant;¶
Chain state.¶
the handle identifies a RAS-accepted hop (Section 6.5) on an active chain;¶
no ancestor subtree is revoked; and¶
the actor lineage that results from collapsing consecutive same-actor
entries, as the onward act will (Section 6.3.7), is within its
depth bound, which counts lineage entries, not hops;¶
Current actor and binding.¶
act is present, conforms to the schema of Section 3.2, and
identifies the current actor, which is the OAuth client the request is
authenticated as (Section 6.3.3);¶
the actor_token_type names a token type the IdP supports, and the
actor_token has a trusted issuer for the actor's domain and tenant, is
valid for that type, is accepted, designates the IdP where applicable,
authenticates the actor, and is sender-constrained to the key confirmed
by the assertion's cnf (Section 6.3.3);¶
the request proves possession of the cnf key with a matching DPoP
proof (Section 6.3.3, [RFC9449]);¶
the actor is permitted by the chain's continuation authorization (Section 6.3.2) to continue from the presented hop; and¶
the IdP can resolve, for the requested audience, both the
audience-local subject and the actor's client identifier
(Section 6.3.3);¶
Freshness and replay.¶
iat is within permitted future clock skew (which SHOULD NOT exceed 60
seconds), exp follows iat, the assertion is unexpired, and its
lifetime does not exceed 300 seconds; and¶
jti is not yet reserved for the assertion issuer, or is RESERVED or
ISSUED under a fingerprint matching this request (permitting idempotent
retry; see Section 6.3.5); a RESERVED or ISSUED jti under a
different fingerprint, or a FAILED jti, is rejected;¶
Envelope containment. The requested audience, resource, scopes, and authorization details are within the root-chain envelope as recorded at establishment and within current IdP actor policy; authorization-details containment uses the comparison rules defined for each authorization-detail type, since [RFC9396] defines no generic comparison, and a detail type whose rules the IdP does not implement is rejected.¶
The reservation model gives a client idempotent recovery after a lost response while preventing one assertion from authorizing more than one distinct request.¶
After validation, grant issuance reserves the assertion's (iss, jti), bound
to a fingerprint of the request it first authorizes, and records the
reservation as RESERVED, ISSUED, or FAILED (distinct from the hop states of
Section 6.5). The fingerprint MUST cover audience as an exact string,
the resource values as an order-independent set, scope as an
order-independent set, the exact authorization_details JSON after form
decoding (a different serialization is a different request), the actor's iss
and sub, the confirmed key's cnf.jkt thumbprint, and a SHA-256 hash of the
exact subject_token after form decoding, which binds the fingerprint to the
specific assertion and its handle. Concurrent redemptions MUST NOT bypass this
binding (Section 8). An identical retry
MUST return the same previously issued grant, not a new one, and a request that
does not match that fingerprint MUST be rejected. Replay uniqueness MUST be
keyed on (iss, jti); partitioning by tenant alone would let two assertion
issuers in one tenant collide on a reused jti.¶
The IdP MUST retain the reservation through exp plus the maximum permitted
clock skew, so an in-window retry is honored; a reservation that does not reach
ISSUED before exp becomes FAILED, which is terminal and requires a fresh
assertion.¶
After a lost response, a client MAY retry the same assertion to recover the ISSUED result or obtain a fresh assertion. A fresh assertion may create an equivalent grant and sibling hop but no additional authority. Application idempotency remains out of scope. Realization guidance is in Section 8.¶
The Token Exchange response follows the base ID-JAG profile: the ID-JAG is
returned in access_token, with token_type N_A.¶
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"issued_token_type": "urn:ietf:params:oauth:token-type:id-jag",
"access_token": "<continuation-capable ID-JAG, compact JWS>",
"token_type": "N_A",
"expires_in": 300
}
¶
The hop reference is delivered as the ID-JAG's identity_continuation_handle
claim (Section 4, rule 1), a claim inside access_token and not a separate
Token Exchange response parameter; the accepting Resource Authorization Server
binds it (Section 6.4), and the domain then surfaces it to continuers as
intra-domain context (Section 6.6). There is likewise no
chain-expiry response parameter: chain lifetime is authoritative at the IdP
(Section 5), and a deployment needing advance warning conveys it through
task or authorization state, an optional ID-JAG claim, or a management API.¶
On success, the IdP records a PENDING child (Section 6.5) of the
presented hop and issues an ID-JAG containing the resolved target sub and
fresh handle. An idempotent retry (rule 6; Section 6.3.5) instead
returns the previously issued grant unchanged, creating no new hop or handle.¶
The onward ID-JAG conforms to the base ID-JAG profile
([I-D.ietf-oauth-identity-assertion-authz-grant]) except where this document
extends it: its sub is the IdP-issued pairwise subject for the target
audience, and aud_sub remains available under the base profile where the
target's native subject namespace differs. The IdP MUST copy the root
authentication context (auth_time, acr, amr) unchanged where
[I-D.ietf-oauth-identity-assertion-authz-grant] requires those claims;
continuation MUST NOT extend or strengthen it, for example by raising acr or
adding amr beyond the user's root authentication. The IdP constructs act
by placing the authenticated current actor atop the presented hop's lineage;
it never copies lineage from the assertion, and siblings do not contribute.
Consecutive identical actors collapse to one entry, though the hop record
remains; policy MAY limit disclosed depth, narrowing what a target sees without
changing the depth bound the IdP enforces (Section 6.3.4, rule 4). Because
policy may narrow the disclosed lineage, a Resource Authorization Server
MUST NOT read the absence of a further nested act as proof that no earlier
actor exists. The following is a
non-normative example of the onward ID-JAG issued by the IdP:¶
{
"iss": "https://idp.example/",
"aud": "https://ras.travel.example/",
"sub": "travel-pairwise-subject",
"client_id": "expense-service",
"resource": "https://api.travel.example/",
"scope": "trips.read",
"identity_continuation_handle": "Uc9fB3mHs5LdK7gEnX2wRj",
"auth_time": 1710000000,
"acr": "urn:example:loa:2",
"amr": ["pwd", "mfa"],
"act": {
"iss": "https://expenses.example/",
"sub": "expense-service",
"act": {
"iss": "https://expenses.example/",
"sub": "expense-app"
}
},
"cnf": {
"jkt": "base64url-current-actor-key-thumbprint"
},
"iat": 1710000025,
"exp": 1710000325,
"jti": "idjag-travel-01"
}
¶
The onward ID-JAG's client_id is the current actor's identifier at the
target RAS.¶
On failure, the IdP returns an OAuth error ([RFC6749], [RFC8693]):¶
it MUST return invalid_continuation (Section 11) only when the handle is
permanently unusable: unknown, on an expired or ended chain, on a revoked
hop or ancestor, or with continuation authorization withdrawn; and¶
it SHOULD use invalid_request for a malformed, inconsistent, or
unacceptable token, invalid_dpop_proof for a DPoP failure, and
invalid_target, invalid_scope, or invalid_authorization_details for a
request outside the envelope.¶
An invalid_continuation handle is terminal: retrying it cannot succeed.
Recovery requires establishing a new chain and succeeds only where the
governing authorization is still continuation-capable: a session-anchored chain
re-roots by re-authenticating the user, a grant-anchored chain from its
still-valid grant without the user, and a handle disabled by withdrawn
continuation authorization cannot re-root at all. The other errors
leave the chain still continuable, so a client abandons only the current
request.¶
Only a RAS from which continuation occurs implements this extension. A terminal RAS processes an ordinary ID-JAG and ignores the handle; because no later continuation uses the terminal hop, the RAS need not bind its handle.¶
A continuation-aware Resource Authorization Server, one that implements this extension and advertises the continuation grant profile (Section 7), MUST, on accepting a continuation-capable ID-JAG:¶
accept the ID-JAG per [I-D.ietf-oauth-identity-assertion-authz-grant], which includes validating the grant, authenticating the presenting client, verifying the sender constraint, applying local authorization policy, and issuing an access token sender-constrained to the confirmed key; and¶
bind identity_continuation_handle, the ID-JAG's issuer and tenant, and
the confirmed key to the authorization state it establishes, recording
whether continuation is permitted.¶
The RAS MUST bind the handle and issue the access token as one outcome: no access token without its binding, and no binding without a token. Repeated redemption of one ID-JAG MUST bind to the same hop authorization record, so a retry cannot create multiple records for one grant. The RAS MUST NOT place the handle in an access token, external authorization claim, or protected-API authorization input. It exposes the binding only privately within its trust domain.¶
A hop moves through three states, spanning the IdP, RAS, and CAI. These are conceptual states, not values carried on the wire. The IdP creates it PENDING. Successful RAS binding makes it ACCEPTED. A mapped CAI attests a hop only once it is ACCEPTED, so a PENDING hop yields no assertion and reaches no continuation exchange. A fresh assertion from the mapped CAI lets the IdP evaluate the hop as CONTINUABLE for one request; CONTINUABLE is not stored but holds only while rules 3, 4, and 5 of Section 6.3.4 hold for that request.¶
| State | Where it lives | Meaning |
|---|---|---|
| PENDING | IdP | the IdP issued the ID-JAG but has no acceptance evidence |
| ACCEPTED | RAS authorization state | the RAS redeemed the grant, authorized it, and bound the handle |
| CONTINUABLE | IdP, for one exchange | a mapped CAI freshly attested the still-active binding |
ACCEPTED is a state of the RAS's own authorization, not an IdP transition delivered by callback; the IdP learns of it only through a CAI attestation.¶
The CAI assertion is trusted evidence of acceptance, not IdP-verifiable proof: the IdP has no channel back to the RAS to confirm acceptance directly (Appendix A.4), so it relies on the mapped CAI having rechecked authoritative RAS state before attesting (Section 6.2). Absent CAI compromise (Section 9.7), an issued-but-rejected ID-JAG cannot be continued because no mapped CAI may attest it. A mapped CAI is mandatory; its absence fails closed.¶
Acceptance gates continuation but does not bound downstream authority: the IdP evaluates later targets against the root envelope, and local RAS authorization neither narrows nor widens it.¶
Within a trust domain, an authorized workload learns the accepted hop's handle from a trusted intra-domain carrier. The carrier:¶
MUST be server-derived and MUST bind the handle, its originating IdP and tenant, and the current credential and key to the RAS authorization;¶
MUST NOT be supplied or overridden by the requester;¶
MUST NOT be accepted outside the trust domain; and¶
MUST be re-derived when replaced.¶
The specific carrier is deployment-specific: a Transaction Token [I-D.ietf-oauth-transaction-tokens] is one realization (Appendix A.2).¶
Before deriving, the protected endpoint or carrier MUST validate live proof of possession of the confirmed key presented on the current call, and MUST derive the handle from the authorization record bound to that verified credential, key, and RAS state, never from a session or subject, which could otherwise bind the wrong user's authorization state to this call.¶
Authorized intra-domain workloads MAY read the handle. They MUST NOT place it in access tokens, external authorization claims, responses, webhooks, errors, or calls to non-participants; deployments also keep it out of logs and traces. The handle conveys no authority.¶
This section is non-normative. It describes ways an IdP can realize this document's requirements; conformance depends only on the normative sections.¶
The replay reservation (Section 6.3.5) is typically held in strongly
consistent state: only one concurrent request reaches ISSUED, a concurrent
request under a matching fingerprint waits for or retries that result, and the
IdP retains and expires the reservation by the same clock it uses to evaluate
exp. A RAS can make the handle binding and token issuance of
Section 6.4 one outcome with a local transaction, or with a
compensating action that revokes a token whose binding did not commit.¶
Because the actor-chain depth bound counts collapsed lineage entries, an actor that repeatedly continues as itself never trips it; the fan-out, rate, and hop-count limits of Section 6.3.2 bound such retry-driven growth instead, and the IdP prunes expired or revoked hop state. A deployment configures those limits and enforces them per governing authorization, so sibling branches of one chain share one budget and a retry cannot escape them.¶
The CAI accounts for retries separately from fan-out and keeps audit records of its issuance and limit enforcement. The IdP performs end-to-end audit correlation across a chain, while each RAS logs only its local subject.¶
An IdP can derive handles from an internal delegation identifier using a keyed one-way function, provided the derived handles still satisfy rules 1, 2, and 5 of Section 4 and remain unlinkable.¶
An IdP can defer materializing chain state until the first continuation, provided the handle still resolves to the same root and envelope; deferral does not relax the reservation durability of Section 6.3.5.¶
This profile assumes TLS, a correct IdP subject map and root-chain envelope, and the OAuth guidance of [RFC9700]. It principally addresses these adversaries:¶
an on-path attacker replaying an assertion (Section 9.3);¶
a compromised intermediate workload broadening authority or continuing the wrong user's chain (Section 9.5);¶
a compromised CAI or actor-token issuer (Section 9.7, Section 9.6);¶
a party influencing the client-to-actor mapping, which on a root exchange
carrying no actor_token is the sole authenticator of the root actor
(Section 6.3.3);¶
a malicious Resource Server or audience attempting cross-domain correlation (Section 10); and¶
a faulty intra-domain carrier (Section 9.5) or a co-located one (Section 9.7).¶
A continuation assertion names the actor the IdP will treat as the chain's
current holder. As a bearer token it would let any party that captured it, in
transit, from a log, or from a compromised intermediary, continue the chain as
that actor. The assertion MUST NOT be accepted as a bearer token [RFC7800];
every exchange requires live proof of possession of the cnf key via a DPoP
proof [RFC9449] (Section 6.3.3). A captured assertion is therefore
useless without the private key, and because the onward ID-JAG is bound to the
same key (Section 6.3.3), possession is demonstrated continuously across
the chain, not once at issuance.¶
Scheduled continuation MUST derive from durable RAS task authorization, not a scheduler-held handle, which would become a durable bearer-like credential outside the per-call key proof and RAS binding that gate every other use. The scheduler holds only a task identifier; each authenticated run re-derives the handle from active task state and still requires an assertion from a mapped CAI.¶
The 300-second ceiling and the single-use (iss, jti) reservation
(Section 6.3.5) confine replay to the IdP continuation exchange. The
request fingerprint bound by that reservation ties each assertion to the one
request it first authorized; without it, a resubmitted assertion could
authorize a second, different request within its window.¶
Downstream resources may gate access on authentication strength (acr) or
methods (amr); if continuation could raise those claims, an actor could reach
a step-up-gated resource the user never authenticated strongly enough for.
Authentication context therefore comes only from the root envelope, copied
unchanged into onward ID-JAGs (Section 6.3.7).¶
The envelope bounds every target and authority. The CAI validates any offline attenuation segment; the IdP still enforces only the envelope. Because the assertion is target-agnostic, a permitted actor may select any target within that ceiling.¶
Wrong-handle association can continue the wrong user's bounded chain. The intra-domain carrier establishes the authoritative association between the request and the handle by deriving it from the current credential's RAS-bound state (Section 6.6); a handle a workload supplies is not authoritative, and the CAI rejects substitution.¶
Keeping CAI issuance in-domain (Section 6.2) prevents a handle-holding party from bypassing the RAS-acceptance path.¶
The actor_token authenticates the current actor to the IdP, so a rogue or
over-scoped actor-token issuer is an impersonation vector: a party controlling
one issuer could mint a token naming an actor in another domain or tenant and
continue that actor's chains. The IdP MUST accept actor tokens only from
issuers trusted for the actor's own domain and tenant, and MUST reject an
untrusted or out-of-scope issuer even when a valid CAI assertion accompanies
it. CAI attestation of the hop and actor-token authentication of the actor are
independent checks (Section 9.7); neither substitutes for the
other.¶
A continuation requires all of these, and no one of them suffices alone:¶
the CAI mapped to the presented hop's accepting Resource Authorization Server, which attests the chain-to-actor transition (Section 6.3.4, rule 3);¶
the workload identity issuer trusted for the current actor's trust domain,
which authenticates the actor through the actor_token (Section 6.3.4,
rule 5);¶
live proof of possession of the confirmed key (Section 6.3.4, rule 5); and¶
the IdP's own root-chain envelope and current-actor policy (Section 6.3.4, rule 7).¶
The IdP MUST authorize CAI and actor-token issuer pairings per
tenant; separate trust in each is insufficient. Tenant determination MUST
derive from authenticated material, not requester-supplied input. The IdP
MUST scope CAI trust by issuer, keys, tenant, and mapped RAS. Because the IdP
records the accepting Resource Authorization Server for each hop, it MAY learn
the candidate mapping from that server's advertised
identity_continuation_issuers (Section 7). That advertisement is a
nomination only: it scopes each server to naming issuers for its own hops but
does not itself establish issuer or key trust. The IdP independently
authenticates each CAI issuer and its signing keys (Section 7), and tenant
policy authorizes the resulting server, CAI, and actor-token-issuer combination.
Absent the advertisement, the mapping is configured out of band.¶
One operator may run the RAS, carrier, and CAI. Co-locating these anchors trades away the defense in depth the conjunction otherwise provides, so where independent acceptance evidence matters, deployments can separate them or audit the binding-to-attestation path. If the IdP is also co-located, even the envelope backstop becomes organizational rather than protocol-separated.¶
A compromised mapped CAI can attest a hop that its Resource Authorization Server refused, or for which it denied continuation, overriding that server's local decision; the envelope still bounds the result, but the accept-and-continue gate (Section 6.5) is only as trustworthy as the mapped CAI. Because the IdP has no channel to recheck RAS state itself, a CAI attesting from a cached read could attest a hop the RAS has since revoked; the authoritative recheck required at issuance (Section 6.2) narrows this window but does not close it, since revocation after issuance still lands within the assertion's lifetime plus any delay in RAS state reaching the CAI.¶
The act lineage records who has acted in the delegation. A compromised actor
could try to forge it, to hide its own identity, impersonate a more privileged
prior actor, or fabricate a delegation that never happened. This profile
denies that by construction: an assertion names only the current actor, and
the IdP builds the onward lineage itself by walking the hop's immutable parent
references (Section 6.3.7), never by copying a chain the assertion
supplies. The IdP MUST reject any mismatch between the current actor and the
assertion's act.
Because lineage derives from IdP-held state rather than assertion input, a
party cannot rewrite history it does not control; offline-attenuation
segments, which the IdP does not observe, do not enter lineage.¶
An attacker may try to pass one token type off as another, downgrade the
signature algorithm, or steer verification to a key it controls. The IdP MUST
verify typ, reject alg=none and symmetric algorithms, and allowlist
asymmetric algorithms. It MUST select keys from trusted issuer configuration;
kid MAY select among them. It MUST NOT trust assertion jku, x5u, embedded
jwk, or other supplied key material.¶
Advertising identity_continuation_issuers (Section 7) in publicly readable
authorization server metadata reveals which CAIs a Resource Authorization
Server authorizes to attest its hops, and can thereby disclose federation
topology, tenant relationships, and deployment structure, the same
disclosure concern the base ID-JAG profile raises for issuer-specific
metadata. A deployment whose CAI
relationships are sensitive SHOULD omit the advertisement and convey the
nomination out of band or through
access-controlled discovery.¶
A hop's identity_continuation_handle is visible only to its ID-JAG client,
the accepting Resource Authorization Server, the IdP, and the domain's
carrier, CAI, and authorized workloads. It never enters an access token,
external authorization claims, or protected-API authorization input
(Section 4, rule 3). A workload receiving it as intra-domain context is a
control-plane participant.¶
Handles are opaque, high-entropy, and hop-specific (Section 4), so they do not provide a common cross-RAS identifier for a user.¶
The chain is not unlinkable: the IdP correlates it, participants sharing a handle can correlate that hop, and actor lineage and timing may correlate transactions across audiences. For example, an observer comparing ID-JAGs issued to two audiences within one short window and carrying the same actor-chain shape may infer they belong to one user's transaction, even without a shared handle.¶
The onward ID-JAG's act chain also names the prior actors to the accepting
RAS outright, with no correlation needed; Section 6.3.7 lets policy limit
the disclosed depth, and deployments may limit the lineage exposed to each
audience.¶
IANA is requested to register the following error in the "OAuth Extensions Error Registry" established by [RFC6749].¶
IANA is requested to register the following value in the "OAuth URI" registry established by [RFC6755] and used for token type identifiers by [RFC8693].¶
urn:ietf:params:oauth:token-type:identity-continuation¶
Token type URI for the Identity Continuation Assertion¶
IETF¶
This document, Section 3.1¶
IANA is also requested to register the following grant-profile value in the same registry.¶
urn:ietf:params:oauth:grant-profile:id-jag-continuation¶
Grant profile identifier for a continuation-capable ID-JAG, whose accepting
Resource Authorization Server binds the identity_continuation_handle claim
to authorization state¶
IETF¶
This document, Section 7, Section 6.4¶
IANA is requested to register the following media type in the "Media Types"
registry, in the manner described in [RFC6838], corresponding to the JOSE
typ header value oauth-identity-continuation+jwt.¶
application¶
oauth-identity-continuation+jwt¶
N/A¶
N/A¶
binary; the +jwt structured syntax suffix [RFC8417] registers this
encoding. An Identity Continuation Assertion is a JWT [RFC7519], a series of
base64url-encoded values (some of which may be empty) separated by period
('.') characters.¶
N/A¶
This document, Section 3.1¶
Applications using OAuth 2.0 Token Exchange [RFC8693] to perform identity continuation across SaaS boundaries.¶
N/A¶
Deprecated alias names for this type: N/A
Magic number(s): N/A
File extension(s): N/A
Macintosh file type code(s): N/A¶
Karl McGuinness (public@karlmcguinness.com)¶
COMMON¶
N/A¶
Karl McGuinness¶
IETF¶
IANA is requested to register the following claim in the "JSON Web Token Claims" registry established by [RFC7519].¶
identity_continuation_handle¶
An opaque, IdP-generated reference to one hop of a continuation chain, used to correlate a continuation to its chain and parent hop and to resolve the per-audience subject. This claim appears in an Identity Continuation Assertion and in a continuation-capable ID-JAG, and its value may also travel in intra-domain chain context; it is not placed in an access token or a Resource Server's external authorization claims (Section 4, rule 3).¶
IETF¶
This non-normative appendix records the principal design choices.¶
The assertion is the Token Exchange input: its audience is the IdP and it has
no top-level sub. The resulting ID-JAG is the target Resource Authorization
Server's grant and contains the IdP-resolved subject and, when applicable, a
continuation handle. The artifacts therefore have different issuers,
audiences, subjects, and consumers.¶
A Transaction Token [I-D.ietf-oauth-transaction-tokens] carries request context within one trust domain. The assertion crosses from that domain to the IdP, is single-use, and carries neither the target subject nor general request context. It may be derived from Transaction Token context, but is not a Transaction Token profile.¶
The choice follows Section 1.1: a pairwise-subject boundary can be crossed only by the IdP, which the target trusts to name the user, and IdP exchange permits current-state and envelope checks at every hop. Direct propagation instead fits deployments with a global subject, shared issuer trust, and no need for mid-chain IdP revocation, such as a single SPIFFE-style trust domain (one workload-identity namespace with no pairwise-subject boundary to cross). Delegated Authorization [I-D.li-oauth-delegated-authorization], whose client-issued tokens carry no subject, composes with this profile as the intra-domain layer and stops where re-issuance to a new subject begins.¶
A pull design would have each target resolve a reference at the IdP over a back channel [RFC7662]. It requires a new target-side grant and per-request back channel. The selected push design reuses the ID-JAG grant path, adding only handle binding at continuation-source RASes; the CAI supplies acceptance evidence. Pull remains a possible companion profile.¶
The signed assertion lets the CAI attest the authenticated actor, key, accepted hop, and any intra-domain policy checks that the IdP cannot observe. It also keeps the IdP's trust coarse: the IdP trusts a CAI mapped to the accepting Resource Authorization Server (the audience the ID-JAG names), scoped per tenant. That CAI abstracts the actors, keys, and continuation policy within its trust domain, so the IdP authorizes mapped CAIs rather than the individual actors behind them. The assertion does not authorize target or scope. Where that domain-local attestation is unnecessary, a recipient-bound direct grant remains a possible simplification, but it forces the IdP to authenticate every actor and hold per-actor policy directly, which does not scale across domains.¶
This profile requires asymmetric signing and forbids encryption and nested signing (Section 3.1), tighter than RFC 8725 [RFC8725], which also permits verified symmetric algorithms. The restriction is deliberate: asymmetric verification avoids distributing a shared secret across domains and the key-confusion risk a symmetric key between the CAI and IdP would create (Section 9.9); TLS on every hop and the assertion's minimal contents make encryption unnecessary; and a single compact signed form removes an interoperability choice between issuers and verifiers.¶
This non-normative appendix illustrates three deployment shapes: interactive application chaining (Appendix B.1), an unattended background agent (Appendix B.2), and a gateway with dynamically selected upstream audiences (Appendix B.3).¶
Message sequences are vertical lifelines with time flowing downward. The payload and state blocks below them are tagged "On the wire" when they cross a trust boundary, "Intra-domain context" when they travel only within one trust domain, and "Server-side state" when they are never transmitted. Continuation handles are written H0, H1, and so on, one per hop. The examples use Transaction Tokens as the intra-domain carrier for illustration only; the profile does not require them (Section 6.6).¶
This section walks the canonical same-IdP flow end-to-end for a single
user: ExpenseApp invokes ExpenseSaaS; ExpenseService, the workload handling
that request, calls TravelAPI to reach TravelSaaS; and TravelService, the
TravelSaaS workload that handles that call, in turn calls BookingAPI to
complete the itinerary. All parties trust one enterprise IdP at
https://idp.example/.¶
The authorization path, the sequence of accepting RASes, differs from that API-call path:¶
ExpenseApp -> ExpenseRAS -> TravelRAS -> BookingRAS¶
Proof of possession uses DPoP. JWTs are shown as decoded payloads; JOSE headers, signatures, and client authentication are omitted. The handle crosses a trust boundary only inside an ID-JAG or Identity Continuation Assertion and travels within a domain only as derived chain context.¶
Participants are grouped by trust domain; all trust the IdP at
https://idp.example/. Each domain from which continuation occurs has two
roles: a Resource Authorization Server that binds the accepted hop and a CAI
that attests continuation, plus a trusted intra-domain carrier, realized here
as a Transaction Token Service (TTS), that derives its chain context. A
deployment may co-locate these.¶
Expense domain (expenses.example): client expense-app, workload
expense-service, and ExpenseRAS / Expense TTS / Expense CAI, in front of
ExpenseAPI.¶
Travel domain (travel.example): workload travel-service, and
TravelRAS / Travel TTS / Travel CAI, in front of TravelAPI.¶
Booking domain (booking.example): BookingRAS and BookingAPI only. It is
terminal in this chain, an ordinary ID-JAG Resource Authorization Server
that needs no continuation support (Section 6.4).¶
Outside the trust circle: PartnerSaaS (partner.example), reached in
Appendix B.1.7.¶
The user has a pairwise subject at each RAS, which only the IdP can map. Handles are H0 at ExpenseRAS, H1 at TravelRAS, and H2 in the ID-JAG addressed to terminal BookingRAS, which never binds it.¶
The root hop establishes the chain and the Expense domain's accepted authorization:¶
ExpenseApp IdP ExpenseRAS ExpenseAPI/TTS
| | | |
|--ID Token---->| | |
|<-ID-JAG(H0)---| | |
|------------------ID-JAG------>| |
|<-------------------AT1--------| bind H0 |
|------------------request + AT1 + DPoP---------->|
| | |<-resolve AT1----|
| | |--bound H0------>|
| | | derive H0 into tctx
¶
Each continuation repeats one exchange. ExpenseService obtains the Travel grant before crossing the boundary:¶
ExpenseService Expense CAI IdP TravelRAS TravelAPI/TTS
| | | | |
|-request H0-->| | | |
|<-assertion---| | | |
|--------------------------->| | |
| assertion + DPoP | | |
|<---------------------------| ID-JAG(H1) | |
|----------------------------------------->| |
| ID-JAG | |
|<-----------------------------------------| AT2; bind H1|
|-----------------request + AT2 + DPoP------------------>|
| | | |<-resolve AT2|
| | | |--bound H1-->|
| | | | derive into TT
¶
Appendix B.1.6 repeats the pattern from TravelSaaS to terminal BookingRAS.¶
ExpenseApp holds an ID Token for the authenticated user and exchanges it at the IdP for an ID-JAG scoped to ExpenseRAS. The request is DPoP-bound to ExpenseApp's key.¶
On the wire (request):¶
POST /token HTTP/1.1 Host: idp.example Content-Type: application/x-www-form-urlencoded DPoP: <proof signed by the expense-app key> grant_type=urn:ietf:params:oauth:grant-type:token-exchange &requested_token_type=urn:ietf:params:oauth:token-type:id-jag &audience=https://ras.expenses.example/ &resource=https://api.expenses.example/ &scope=expenses.read &subject_token=<id_token> &subject_token_type=urn:ietf:params:oauth:token-type:id_token &actor_token=<sender-constrained expense-app credential> &actor_token_type=urn:ietf:params:oauth:token-type:jwt¶
The IdP resolves the ID Token's sid to the anchoring session and verifies
ExpenseApp's actor credential and DPoP key. Existing consent and enterprise
policy permit continuation to Expense, Travel, and Booking by the designated
workloads, so the IdP records this root-chain envelope:¶
Server-side state:¶
(https://ras.expenses.example/, https://api.expenses.example/)
permitted scopes: expenses.read
(https://ras.travel.example/, https://api.travel.example/)
permitted scopes: trips.read
(https://ras.booking.example/, https://api.booking.example/)
permitted scopes: stays.book
¶
The envelope also records the governing authorization, permitted continuers, and expiry. A deployment with unknown onward targets records an authorization-basis ceiling instead and evaluates each target at continuation time (Section 6.3.4, rule 7).¶
The IdP creates a fresh root hop, H0, for this chain and embeds it as a claim of the ID-JAG it is about to issue (Section 4, rule 1); the hop is PENDING until a Resource Authorization Server accepts it (Section 6.5). The Token Exchange response carries the ID-JAG and no continuation-specific response member; H0 travels inside the ID-JAG.¶
The decoded ID-JAG for ExpenseRAS carries the user's ExpenseRAS-local subject and the root hop's handle.¶
On the wire (decoded ID-JAG):¶
{
"iss": "https://idp.example/",
"aud": "https://ras.expenses.example/",
"sub": "expense-pairwise-subject",
"client_id": "expense-app",
"resource": "https://api.expenses.example/",
"scope": "expenses.read",
"auth_time": 1710000000,
"acr": "urn:example:loa:2",
"amr": ["pwd", "mfa"],
"identity_continuation_handle": "kW4uJ8pTe2NxA6rQvD1zYs",
"cnf": {
"jkt": "base64url-expense-app-key-thumbprint"
},
"iat": 1710000005,
"exp": 1710000305,
"jti": "idjag-expense-01"
}
¶
ExpenseApp exchanges this ID-JAG at ExpenseRAS for an access token (AT1),
exactly as for any ID-JAG [I-D.ietf-oauth-identity-assertion-authz-grant]
(not shown), except that ExpenseRAS also recognizes the continuation grant
profile and processes identity_continuation_handle (Section 6.4).
ExpenseRAS validates the ID-JAG, authenticates ExpenseApp, verifies the
DPoP proof, and applies its local policy; only if every check and the
access-token issuance itself succeed does it atomically bind H0 to the
authorization state behind AT1, moving the hop from PENDING to ACCEPTED. A
hop that never reaches ACCEPTED, for example one copied from an ID-JAG
that ExpenseRAS rejected, is not usable: no CAI attests a hop
that its Resource Authorization Server never accepted.¶
ExpenseRAS keeps this association in a private internal record. It is never serialized into AT1, an external authorization claim, or anything that ExpenseAPI's callers observe.¶
Server-side state:¶
{
"identity_continuation_handle": "kW4uJ8pTe2NxA6rQvD1zYs",
"status": "ACCEPTED",
"authorization_state": "at1-authz-2f9c",
"client_id": "expense-app",
"bound_at": 1710000010
}
¶
ExpenseApp calls ExpenseAPI with AT1. The Expense TTS, ExpenseSaaS's own Transaction Token Service, resolves AT1 against the record that ExpenseRAS just created over their shared, own-domain interface (Section 6.4), derives H0 from it, and issues a local Transaction Token for ExpenseService, the workload that will complete the request.¶
Intra-domain context (decoded Transaction Token):¶
{
"iss": "https://tts.expenses.example/",
"aud": "https://expenses.example/",
"sub": "expense-pairwise-subject",
"txn": "txn-expense-88f2",
"scope": "expense-report:complete",
"req_wl": "expense-api",
"tctx": {
"identity_continuation": {
"iss": "https://idp.example/",
"tenant": "tenant-123",
"handle": "kW4uJ8pTe2NxA6rQvD1zYs"
}
},
"iat": 1710000012,
"exp": 1710000072,
"jti": "tt-expense-0007"
}
¶
The Expense TTS derives this context from AT1's authorization record; neither
ExpenseApp nor ExpenseService supplies H0. The Transaction Token remains
inside expenses.example and is normally forwarded unchanged within that
domain. A replacement token requires the TTS to re-derive the member
(Section 6.6). The tctx.identity_continuation encoding
shown is illustrative and deployment-defined; this document standardizes no
carrier schema.¶
ExpenseService asks its own CAI for an assertion covering H0. Before issuing, Expense CAI authenticates ExpenseService, verifies its key, confirms that H0 belongs to the transaction that ExpenseService is serving, and rechecks that ExpenseRAS's authorization remains active. The IdP's per-hop map designates Expense CAI to attest hops accepted by ExpenseRAS (Section 6.2, Section 6.3.2).¶
On the wire (decoded assertion):¶
{
"iss": "https://cai.expenses.example/",
"aud": "https://idp.example/",
"identity_continuation_handle": "kW4uJ8pTe2NxA6rQvD1zYs",
"act": {
"iss": "https://expenses.example/",
"sub": "expense-service"
},
"cnf": {
"jkt": "base64url-expense-service-key-thumbprint"
},
"iat": 1710000020,
"exp": 1710000200,
"jti": "b8Rn5Yx1Qe4Nk2Wf6zVc9d"
}
¶
ExpenseService presents the assertion to the IdP as the subject_token,
DPoP-bound to its own key.¶
On the wire (request):¶
POST /token HTTP/1.1 Host: idp.example Content-Type: application/x-www-form-urlencoded DPoP: <proof signed by the expense-service key> grant_type=urn:ietf:params:oauth:grant-type:token-exchange &requested_token_type=urn:ietf:params:oauth:token-type:id-jag &audience=https://ras.travel.example/ &resource=https://api.travel.example/ &scope=trips.read &subject_token=<identity-continuation-assertion> &subject_token_type=urn:ietf:params:oauth:token-type:identity-continuation &actor_token=<sender-constrained expense-service credential> &actor_token_type=urn:ietf:params:oauth:token-type:jwt¶
The IdP runs the checks of Section 6.3.4: the DPoP key matches both the
assertion's cnf.jkt and the actor token's key confirmation; expense-service
is the
actor named in act; H0 is CONTINUABLE; and the requested TravelRAS,
TravelAPI, and trips.read values match the Travel target entry in the
root-chain envelope. The IdP does not call ExpenseRAS to confirm acceptance.
Instead, the assertion from ExpenseSaaS's mapped CAI,
https://cai.expenses.example/, is the evidence that H0 reached ACCEPTED state
and is CONTINUABLE (Section 6.5).¶
The IdP resolves the user's TravelRAS pairwise subject and creates H1 as a
child of H0. The decoded ID-JAG carries H1 and the newly constructed
act chain (Section 6.3.7): expense-service, authenticated at this
exchange, placed atop the root actor expense-app. travel-service has
not yet performed an exchange, so it is not part of the lineage.¶
On the wire (decoded ID-JAG):¶
{
"iss": "https://idp.example/",
"aud": "https://ras.travel.example/",
"sub": "travel-pairwise-subject",
"client_id": "expense-service",
"resource": "https://api.travel.example/",
"scope": "trips.read",
"auth_time": 1710000000,
"acr": "urn:example:loa:2",
"amr": ["pwd", "mfa"],
"identity_continuation_handle": "Uc9fB3mHs5LdK7gEnX2wRj",
"act": {
"iss": "https://expenses.example/",
"sub": "expense-service",
"act": {
"iss": "https://expenses.example/",
"sub": "expense-app"
}
},
"cnf": {
"jkt": "base64url-expense-service-key-thumbprint"
},
"iat": 1710000025,
"exp": 1710000325,
"jti": "idjag-travel-01"
}
¶
ExpenseService exchanges the TravelRAS ID-JAG at TravelRAS for an access token (AT2), presenting a fresh DPoP proof with the same expense-service key. TravelRAS recognizes the continuation grant profile just as ExpenseRAS did: it validates the ID-JAG, authenticates ExpenseService, and, on success, atomically binds H1 to the authorization state behind AT2, exactly as Appendix B.1.2 describes for ExpenseRAS and H0.¶
ExpenseService calls TravelAPI with AT2. The Travel TTS derives H1 from that bound state and issues a local Transaction Token for TravelService, the TravelSaaS workload that receives the request. Its chain-context member differs from the Expense token only in the hop handle:¶
Intra-domain context (excerpt):¶
"tctx": {
"identity_continuation": {
"iss": "https://idp.example/",
"tenant": "tenant-123",
"handle": "Uc9fB3mHs5LdK7gEnX2wRj"
}
}
¶
The token remains inside travel.example; H1 replaces H0 because TravelRAS,
not ExpenseRAS, is now the accepted authorization from which continuation
will occur.¶
TravelService needs a reservation from BookingSaaS. Processing the request
whose Transaction Token carries H1, it obtains the same assertion shape as
Appendix B.1.3 from Travel CAI, now naming H1, travel-service, and
TravelService's confirmed key.¶
TravelService exchanges the assertion, DPoP-bound to its own key, for an
ID-JAG with audience=https://ras.booking.example/,
resource=https://api.booking.example/, and scope=stays.book, all
within the envelope's Booking target entry. The IdP creates a fresh hop
H2 whose immutable parent is H1 and constructs the onward act chain
(Section 6.3.7): travel-service, authenticated at this exchange,
placed atop the presented hop's lineage (expense-service, then
expense-app).¶
On the wire (selected claims from the decoded ID-JAG):¶
{
"aud": "https://ras.booking.example/",
"sub": "booking-pairwise-subject",
"client_id": "travel-service",
"resource": "https://api.booking.example/",
"scope": "stays.book",
"identity_continuation_handle": "Ht6mZ2pQe8VrKx4NcWy1Jd",
"act": {
"iss": "https://travel.example/",
"sub": "travel-service",
"act": {
"iss": "https://expenses.example/",
"sub": "expense-service",
"act": {
"iss": "https://expenses.example/",
"sub": "expense-app"
}
}
}
}
¶
TravelService redeems the ID-JAG at BookingRAS for an access token (AT3), presenting a fresh DPoP proof with the same key. Because Booking is terminal, BookingRAS follows the ordinary ID-JAG profile: it ignores H2, issues AT3, and does not bind the hop (Section 6.4). Only ExpenseRAS and TravelRAS, the Resource Authorization Servers from which continuation occurs, implement the binding extension. TravelService then calls BookingAPI with AT3.¶
TravelService itself is the current-domain actor that obtains the next ID-JAG; it does not pass the handle to a sibling workload (Section 6.6).¶
Suppose TravelSaaS must also call PartnerSaaS at https://partner.example/,
whose Resource Authorization Server does not trust idp.example. The chain
cannot continue there: the IdP holds no pairwise subject for that audience
and no authorization basis covers it, so a continuation request for that
target fails (Section 6.3.4, rules 5 and 7; invalid_target). This is
the profile's boundary, not a deployment error: continuation serves the set
of Resource Authorization Servers that trust the common IdP.¶
A separate identity-chaining profile can cross that boundary under a bilateral trust agreement. For example, TravelService can present its Transaction Token to the Travel-domain authorization server under [I-D.fletcher-transaction-token-chaining-profile], which issues a minimized grant for PartnerSaaS. The Transaction Token and continuation handle stay in the Travel domain; neither is sent to PartnerSaaS.¶
The user is present when the task is created and absent at every run. Unlike the interactive example, the root hop is bound to durable, platform-owned task authorization. The Scheduler stores only an opaque task identifier; each run derives fresh context from the active authorization (Section 9.2).¶
Platform domain (platform.example): workload briefing-agent, and
PlatformRAS (the platform's own TaskRAS) / Platform TTS / Platform CAI,
in front of TaskAPI (https://api.platform.example/tasks);
the Scheduler is an internal platform component, holding only the task
identifier, that triggers each run.¶
Calendar domain (calendar.example): CalendarRAS only, in front of
CalendarAPI. It is terminal in every run.¶
Mail domain (mail.example): MailRAS in front of MailAPI, reached only
in the dynamic-target scenario below (Appendix B.2.3); likewise
terminal.¶
The Scheduler stores only task-123. H0 remains bound to the PlatformRAS
task authorization across runs; each run receives a fresh child of H0 for
its terminal target.¶
Alice authorizes "summarize my calendar every morning." Because the task must
outlive her session, briefing-agent uses a refresh token from a
continuation-capable grant as the root exchange's subject token. The chain
is therefore anchored to that grant, not Alice's current session
(Section 6.3.2, Section 5). The root ID-JAG targets the
PlatformRAS; the envelope records both that root target and the
Calendar target needed by the task.¶
Server-side state (root envelope excerpt):¶
(https://ras.platform.example/, https://api.platform.example/tasks)
permitted scopes: task.manage
(https://ras.calendar.example/, https://api.calendar.example/)
permitted scopes: calendar.read
¶
The response and RAS-binding pattern match Appendix B.1.1 and Appendix B.1.2; the request differs by using a refresh token to obtain a grant-anchored chain. PlatformRAS binds H0 to the durable task authorization.¶
PlatformRAS keys the resulting durable task authorization by its assigned task identifier. The record holds no bearer credential.¶
Server-side state (PlatformRAS task authorization):¶
task_id: task-123 owner: alice actor: briefing-agent continuation_handle: Pz6vTq1NcY4kM8bJf3RxWa # H0 permitted_purpose: morning-calendar-brief schedule: "0 7 * * *" governing_grant: grant-8f2c19a4 # internal reference expiry: 1719450000 # local, not IdP lifetime status: active¶
Server-side state (Scheduler):¶
task_id: task-123¶
The Scheduler never receives, stores, or transmits H0 or any user, chain, or
bearer credential;
task-123 identifies a row in PlatformRAS's own durable state and means
nothing outside the platform.¶
Each run first authenticates the trigger and derives H0 from active task state:¶
Scheduler BriefingAgent Platform TTS
| | |
|---trigger--->| | task-123
| |-task-123+proof->|
| | | verify proof + task; derive H0
| |<-fresh TT(H0)---|
¶
BriefingAgent then performs a fresh continuation to terminal CalendarRAS:¶
BriefingAgent Platform CAI IdP CalendarRAS
| | | |
|--request H0-->| | |
|<-assertion----| | |
|---------------------------->| |
| assertion + DPoP | |
|<----------------------------| ID-JAG(child) |
|-------------------------------------------->|
| ID-JAG |
|<--------------------------------------------| access token
| | | no binding (terminal)
¶
The task identifier is not a secret and does not authorize a run. The
Scheduler's trigger authenticates and carries only task-123; BriefingAgent
then authenticates to the Platform TTS and proves possession of its key, and
the TTS, after confirming task-123 is active and BriefingAgent is its
designated actor, derives H0 into fresh intra-domain context
(Section 6.6). Neither the Scheduler nor BriefingAgent
selects H0.¶
Before issuing, Platform CAI authenticates briefing-agent, verifies its key
and transaction, and rechecks that PlatformRAS's H0 authorization remains
active. The assertion and onward ID-JAG have the shapes shown in
Appendix B.1.3 and Appendix B.1.4.¶
Each run presents H0 and receives a different child. CalendarRAS is terminal, so it issues the access token without binding that child. A later run's child is a sibling, not a descendant, of the earlier run's child (Section 4, Section 6.4).¶
Had this run also needed https://api.mail.example/ behind
https://ras.mail.example/ (Appendix B.2.3), briefing-agent would
present H0 again for a second assertion and receive a second, independent
child for MailRAS. MailRAS is also terminal and does not bind it. The Mail
and Calendar children share H0 as their parent; neither carries the other's
lineage.¶
Suppose the platform later extends the briefing to include unread mail,
which requires https://api.mail.example/ behind
https://ras.mail.example/: a target nobody named when Alice created the
task. Under the target entries recorded in the setup above, a run's
continuation exchange presenting H0 for that audience fails, and the
chain is otherwise unaffected.¶
On the wire (response):¶
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"error": "invalid_target"
}
¶
For a deployment that expects dynamic targets, the envelope's basis is
Alice's standing consent as recorded when the chain was established (for
example, a productivity read-access grant) and tenant policy, with no
enumerated targets; the IdP evaluates each dynamic target against that
recorded basis at continuation time (Section 6.3.4, rule 7). A scope granted
only later does not extend this chain. The same exchange succeeds only if
read access to the mail service is within Alice's standing consent and tenant
policy permits briefing-agent to reach it. A request for mail.send,
outside that consent, fails with invalid_scope.¶
The establishment-time envelope remains the ceiling: later policy may narrow or revoke it but cannot broaden it. A target-specific failure leaves the chain continuable for other authorized targets.¶
Stealing task-123 reveals no handle and does not authorize a trigger.¶
Stealing the internal task record exposes H0, but H0 alone is insufficient: continuation still requires the agent key and an assertion from Platform CAI while the PlatformRAS authorization remains active.¶
The ID-JAG, local task authorization, and IdP-held chain have distinct lifetimes (Section 5).¶
This pattern requires a user-present setup event to root the chain. Where no such event exists (for example, an administratively mandated agent acting for users who never authorized it), there is no delegation to continue and this profile does not apply; such deployments need a differently rooted authorization, such as administrative policy at the IdP, which is out of scope for this document.¶
AgentApp knows the gateway audience but not the eventual upstream. The gateway knows the upstream but holds no end-user assertion addressed to it. This flow lets the gateway obtain an audience-specific grant without weakening the original assertion's audience check.¶
AgentPlatform domain (agent.example): client agent-app only, the
confidential runtime that hosts Alice's session and roots the chain; it
has no Resource Authorization Server of its own in this example.¶
Gateway domain (gateway.example): workload tool-gateway, and
GatewayRAS / Gateway TTS / Gateway CAI, in front of the gateway's
own tool-invocation surface (resource=https://gateway.example/),
scoped under tenant tenant-gw-01.¶
Wiki domain (wiki.example): WikiRAS only, in front of WikiAPI. It is
terminal in this chain.¶
Alice has pairwise subjects at GatewayRAS and WikiRAS, which only the IdP can map. H0 is the root hop bound at GatewayRAS; H1 is the terminal Wiki hop.¶
The runtime roots the chain at the gateway:¶
AgentApp IdP GatewayRAS
| | |
|--ID Token---->| |
|<--ID-JAG(H0)--| |
|----------------ID-JAG-------->| bind H0
|<-------------gateway AT-------|
¶
To reach Wiki, the gateway continues the chain:¶
ToolGateway Gateway CAI IdP WikiRAS/API
| | | |
|--request H0---->| | |
|<-assertion------| | |
|------------------------------>| |
| assertion + DPoP | |
|<------------------------------| ID-JAG(H1) |
|------------------------------------------------>|
| ID-JAG to WikiRAS |
|<------------------------------------------------| wiki AT
|--------------------call WikiAPI with AT-------->|
| | | no binding (terminal)
¶
AgentApp performs a root exchange for the one audience it knows: GatewayRAS.
The eventual upstreams are not known at root time, so, unlike the interactive
example (Appendix B.1), whose envelope enumerated each onward target, this
envelope records an authorization-basis ceiling, Alice's standing consent and
tenant policy, with
no enumerated targets; enterprise policy permits tool-gateway to continue it
(Section 6.3.2, Section 6.3.4, rules 5 and 7). GatewayRAS accepts the
ID-JAG and binds H0 exactly as ExpenseRAS bound H0 in Appendix B.1.2.¶
AgentApp then invokes the gateway with its access token and no continuation input. AgentApp can read H0 in its ID-JAG, but it cannot supply or select the handle used for this call. Gateway TTS derives H0 from the authorization that GatewayRAS bound to the presented access token (Section 6.6).¶
Resolving the tool call, the gateway selects Wiki as the upstream, a target no
one enumerated when AgentApp rooted the chain. ToolGateway reads H0 from its
transaction context, obtains an assertion from Gateway CAI, and presents it to
the IdP with a DPoP proof of the cnf key, as in Appendix B.1.4, now
requesting
audience=https://ras.wiki.example/, resource=https://api.wiki.example/, and
scope=wiki.read.¶
Because the envelope enumerates no targets, the IdP evaluates this dynamically
chosen target against the recorded basis, Alice's standing consent and tenant
policy at establishment (Section 6.3.4, rules 5 and 7). Wiki read access is
within
that basis and enterprise policy permits tool-gateway to reach it, so the
exchange succeeds and the IdP constructs the onward lineage with tool-gateway
atop agent-app. A target hint from the gateway informs issuance limits and
logging only; the IdP, not the gateway, decides whether a target is in the
envelope.¶
WikiRAS is terminal and redeems the resulting ID-JAG without binding H1. Each
permitted tool call repeats this exchange and creates a sibling hop under H0; a
target outside the basis fails with invalid_target as in Appendix B.2.3.¶
This non-normative appendix lists unresolved design questions.¶
[[ To be removed before publication as an RFC ]]¶
Signed assertion versus a recipient-bound direct profile.
Could the IdP bind a continuation credential to an intended actor, actor
class, trust domain, or key and accept it with client authentication,
sender-constrained actor_token, and live key proof? Are the CAI's actor/key
attestation and domain-local gate worth the added trust configuration
(Appendix A.5)?¶
Mutual-TLS binding. Should this profile and ID-JAG add mutual-TLS binding together (Section 6.3.3)?¶
A client establishment parameter. Should a client be able to require or suppress chain establishment, or negotiate lifetime, depth, or permitted continuers (Section 6.3.2)?¶
CAI issuance. Should the document define an interoperable token-endpoint-style issuance request (Section 6.2)?¶
POST /identity-continuation-assertion HTTP/1.1 DPoP: <proof> identity_continuation_handle=<handle>¶
The authenticated workload and proof key would determine act and cnf,
and the IdP audience would derive from the hop's binding rather than be
supplied.
The profile could also define errors, discovery, retry, and optional
target/resource constraints enforced by the IdP as ceilings. The CAI's
signing-key discovery is already specified (Section 7), so this item
concerns only the issuance request and response.¶
Authorization-basis representation. Should the envelope expose a testable representation of the authorization ceiling, for example:¶
{ "targets": [ { "audience": "https://ras.travel.example/",
"resource": "https://api.travel.example/",
"scope": ["trips.read"] } ] }
¶
Dynamic ceilings might instead use an authorization detail [RFC9396], policy-bound intent, or immutable policy artifact. Should continuation permission have a dedicated consent scope even though establishment can occur without a client-requested scope?¶
Further questions are tracked in the project's issue list rather than expanded
here: nested own-domain act segments and offline-actor audit
([I-D.mcguinness-oauth-actor-receipts],
[I-D.mcguinness-oauth-actor-proofs]); a pull topology with target-side
resolution (Appendix A.4); an alternative chain-context carrier that
derives the handle from RAS-bound state; IdP discovery metadata for accepted
actor-token types, issuers, and proof methods (Section 7); a non-user root
profile (Section 1.1); and RAS-derived narrowing with a signed
intersection model (Section 6.5).¶
The author thanks the authors of the OAuth Identity and Authorization Chaining Across Domains and the Identity Assertion JWT Authorization Grant, on whose work this profile builds.¶
[[ To be removed before publication as an RFC ]]¶
-01¶
Renamed Chain Authority to Continuation Assertion Issuer and the
direct/chained exchanges to root/continuation exchanges, and aligned with the
base ID-JAG profile: terminology (IdP Authorization Server), Token Exchange
request/response formatting, and resource cardinality (zero or more, per
RFC 8707).¶
Restructured for clarity and scope: grouped request validation into seven rules; split the response into success, onward ID-JAG construction, and errors; added a non-normative Implementation Considerations section; and demoted the intra-domain carrier and other deployment guidance out of normative text.¶
Bound the originating IdP and tenant to the accepted hop, so the CAI derives the assertion audience from that binding rather than requester input; restricted CAI issuance to the RAS trust domain.¶
Made the RAS identity_continuation_issuers advertisement a nomination only
(the IdP establishes issuer trust and keys independently, resolving an
authorization-server CAI's keys from its jwks_uri) and added a Metadata
Disclosure security consideration.¶
Tightened the security model: relocated the replay-fingerprint and
authentication-context requirements into the protocol sections with their
rationale in Security; narrowed invalid_continuation to permanently
unusable handles; distinguished the ID-JAG, assertion, and access-token
lifetimes; clarified that a depth-limited act is not proof of complete
lineage; and made chain revocation testable.¶
Rewrote the Introduction; corrected the examples and cross-references, expanded the root-chain envelope and design rationale, softened the handle-correlation claim, trimmed the open items, and marked the draft an individual submission.¶
-00¶
Initial revision¶