| Internet-Draft | Identity Continuation Assertion | September 2026 |
| McGuinness & Parecki | Expires 13 March 2027 | [Page] |
This document defines the Identity Continuation Assertion, a short-lived, sender-constrained JSON Web Token (JWT) used as an OAuth 2.0 Token Exchange subject token. It enables a workload acting on a user's behalf to obtain an Identity Assertion JWT Authorization Grant (ID-JAG) for another service when it lacks a suitable credential, including when the user is no longer present.¶
A trusted issuer attests that a resource authorization server accepted an earlier ID-JAG and that the resulting authorization remains active and eligible for continuation. The workload exchanges this assertion at the identity provider, which evaluates the requested access under the chain authorization and current policy before issuing an onward ID-JAG. The profile supports multi-hop access across resource authorization servers that trust a common identity provider.¶
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 13 March 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.¶
The Identity Assertion JWT Authorization Grant (ID-JAG) [I-D.ietf-oauth-identity-assertion-authz-grant] allows an application to exchange the user's identity assertion at the IdP Authorization Server (IdP) for a grant that it redeems at a target Resource Authorization Server (RAS) for an access token. A service receiving that access token may need to call a further service on the user's behalf, including when the user is no longer present. It may hold neither the user's identity assertion nor another credential accepted by the next authorization server.¶
This profile addresses deployments in which RASes trust a common IdP. The receiving workload's incoming access token is not accepted at the next RAS. When pairwise subject identifiers are used, the workload may also be unable to determine the user's subject at that RAS. This profile enables multi-hop access when the request's path is not known in advance, such as at a Model Context Protocol (MCP) tool gateway (Appendix B.1).¶
This document defines the Identity Continuation Assertion, a short-lived, sender-constrained JSON Web Token (JWT) [RFC7519] that the workload obtains from a Continuation Assertion Issuer (CAI). The IdP trusts the CAI to attest the following (Section 5.4):¶
The RAS accepted the referenced ID-JAG, established an authorization from it, and that authorization remains active and is eligible for continuation.¶
The authenticated workload is associated with that authorization context.¶
The CAI binds the assertion to the key the workload proves to it (Section 5.4.1). The accepting RAS may also perform the CAI role.¶
The workload presents the assertion to the IdP as the subject token of an OAuth 2.0 Token Exchange [RFC8693] request. The assertion identifies the accepted authorization and the current actor; the continuation request, not the assertion, names the target and the requested authority. The IdP authenticates the workload, verifies possession of the assertion's bound key, evaluates the requested access, and resolves the user's subject for the target RAS. If authorized, it issues an onward ID-JAG that the workload redeems at that RAS (Section 5.5). The assertion conveys no downstream authority.¶
Each ID-JAG issued in a chain represents a hop. A root ID-JAG and the hops descending from it form a chain. An opaque continuation handle identifies each ID-JAG's hop within a chain (Section 4). The IdP records the relationships between hops and associates each chain with the authorization established at its root exchange. Each continuation is evaluated under that authorization and current policy (Section 5.1.4). RAS acceptance enables continuation; incoming access-token scopes do not automatically limit authority at another target.¶
A workload that continues once can reuse the resulting access token for further calls, subject to the conditions in Section 8.¶
This document extends ID-JAG, referred to as the base profile, and complements OAuth Identity Chaining [I-D.ietf-oauth-identity-chaining]. This profile does not replace mechanisms for narrowing an existing token within one trust domain (Appendix A.1).¶
The IdP, the continuing workload, a RAS from which workloads continue, and the CAI implement this extension. Root clients use the base exchange; chain establishment requires a resolvable session anchor or, optionally, a grant anchor (Section 6.1), and without one the IdP issues an ordinary ID-JAG without a handle. A terminal RAS needs only the base profile's support for redeeming a DPoP-bound ID-JAG (Section 5.5.5). This document defines no new access-token format.¶
The following flow illustrates one continuation. AgentApp calls a tool gateway on Alice's behalf, and the gateway calls a wiki API for her. Appendix B.1 provides the corresponding requests, responses, and tokens.¶
Alice authenticates at the IdP. The gateway and wiki have separate RASes. The gateway is the continuing workload, and its RAS also performs the CAI role (Section 8.1).¶
Steps are numbered as in the figure; "as in ID-JAG" marks what is unchanged from the base profile, "new" what this profile adds.¶
AgentApp IdP GatewayRAS ToolGateway WikiRAS | | | | | | (1) exchange ID Token for ID-JAG | | |---------->| | | | | ID-JAG with handle H0 [new claim] | | |<----------| | | | | (2) present ID-JAG (jwt-bearer grant) | | |------------------------>| | | | access token; RAS binds H0 to it [new] | |<------------------------| | | | (3) call: access token | | |-------------------------------------->| | | | | | | | | | (4) exchange access token: assertion [new] | | |<------------| | | | | assertion: H0 accepted, active, eligible [new] | | |------------>| | | | (5) exchange assertion for next ID-JAG [new] | |<--------------------------| | | | ID-JAG for wiki, handle H1 (child of H0) | | |-------------------------->| | | | | | (6) present ID-JAG | | | |---------------->| | | | | access token; base profile | | | |<----------------|¶
As in ID-JAG, AgentApp exchanges Alice's ID Token at the IdP for an ID-JAG
for the gateway's RAS (GatewayRAS in the figure). New: the IdP records the
root hop, H0, and its chain authorization, and includes H0's handle in the
ID-JAG's identity_continuation_handle claim (Section 5.1,
Section 4).¶
As in ID-JAG, AgentApp presents the ID-JAG to the gateway's RAS and receives an access token. New: the RAS binds the handle to the authorization it creates (Section 5.2). A RAS that does not implement this profile ignores the claim.¶
AgentApp calls the gateway (ToolGateway) with the access token.¶
New: the gateway exchanges that access token at its RAS's token endpoint for an Identity Continuation Assertion (Section 3). The RAS resolves the token's hop binding and attests H0's activity, eligibility for continuation, and association with the gateway (Section 5.4).¶
New: the gateway presents the assertion to the IdP as the subject_token
of a continuation exchange, with its own credential and a DPoP proof
(Section 5.5). The IdP checks the wiki and the gateway against what
it recorded in step 1 and current policy, resolves Alice's subject for the
wiki, and issues an ID-JAG as hop H1, a child of H0 (Section 5.5.3).¶
As in ID-JAG, the gateway presents the ID-JAG to the wiki's RAS (WikiRAS) and receives an access token. New: if that RAS implements this profile it binds H1 as in step 2 and the chain can continue; otherwise it ignores the handle and the chain ends there.¶
In step 4, the CAI attests acceptance and activity for the hop's RAS. In step 5, the IdP uses that attestation to evaluate continuation and resolve Alice's subject for the wiki.¶
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, listed alphabetically:¶
The number of entries in the actor lineage the IdP derives from its own
hop records, after consecutive entries for the same actor are merged and
before any narrowing of what the onward act discloses
(Section 5.5.5). Tenant policy bounds it per branch
(Section 6.3).¶
An IdP-held tree of hops under one chain authorization; each hop's parent reference gives the tree its shape (Section 5.5.5), and the authorization bounds its lifetime (Section 6).¶
The tenant's authorization decision recorded by the IdP at the root exchange and associated with the chain's lifecycle anchor (Section 6.1). It determines which actors may continue and what authority they may obtain, subject to current policy (Section 5.1.4).¶
The role the IdP trusts to issue Identity Continuation Assertions for a tenant and the RAS whose hops it attests (Section 5.4).¶
identity_continuation_handle):An opaque, unguessable, IdP-generated reference to one hop of a chain (Section 4).¶
Describes an ID-JAG that carries the identity_continuation_handle claim
(Section 4).¶
The workload presenting the assertion to the IdP, named by act. Its
canonical actor identity is the (iss, sub) pair that its authentication
to the IdP resolves to (Section 5.5.2).¶
A chain anchored to a refresh token's OAuth grant rather than to the user's IdP session, so that it can continue after logout (Section 6.1).¶
One link of a chain: the IdP's record of an ID-JAG it issued, with an immutable reference to its parent hop unless it is the root (Section 5.5.5). Its hop lineage is its path to the root; a hop from which no workload continues is terminal.¶
An Identity Assertion JWT Authorization Grant [I-D.ietf-oauth-identity-assertion-authz-grant] issued for a target RAS.¶
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 (Section 3).¶
The authority that authenticates the user, determines the user's subject identifier for each target RAS, and issues onward grants.¶
A subject identifier specific to a RAS or group of RASes, allowing the same user to have different identifiers at different audiences.¶
An Authorization Server that protects a particular API, trusts the IdP for subject resolution, and exchanges an ID-JAG for an API access token. [I-D.ietf-oauth-identity-assertion-authz-grant] abbreviates this role (AS).¶
The administrative boundary within which the chain and CAI trust are configured; its determination is deployment-defined, derived from authenticated material, never from requester-supplied input (Section 7.3).¶
An administrative and authentication boundary within which workloads can be directly authenticated, comparable to Workload Identity in Multi System Environments (WIMSE) [I-D.ietf-wimse-arch]. Its identifier is deployment-defined.¶
A service that received a request on a user's behalf and may continue it to a further service.¶
A CAI issues the assertion (Section 5.4); the IdP validates it (Section 5.5.3).¶
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.
The CAI MUST sign the assertion with an asymmetric algorithm the IdP accepts
(Section 9.7). 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": 1710000020,
"exp": 1710000200,
"jti": "b8Rn5Yx1Qe4Nk2Wf6zVc9d"
}
¶
The claims have the following meanings and requirements:¶
iss:REQUIRED. The CAI that issued the assertion; the IdP verifies its issuer trust per the issuer-trust rule of Section 5.5.3, and its signature per the well-formedness rule.¶
aud:REQUIRED. A single string exactly matching the IdP issuer identifier: not its token endpoint URL, and not the requested target.¶
identity_continuation_handle:act:REQUIRED. The current actor presenting the Token Exchange request, encoded
as a single-level act claim per [RFC8693]:¶
iss and sub are REQUIRED, non-empty strings: the actor's canonical
actor identity as Section 5.5.2 defines it. Only iss and sub
form the canonical actor identity under this document.¶
Additional members MAY carry further information about the actor but MUST NOT affect processing defined by this document unless another specification profiles their use.¶
A recipient MUST ignore members it does not understand.¶
exp, nbf, aud, scope, cnf, and a nested act MUST NOT be
present; the IdP rejects an assertion whose act carries one (the
well-formedness rule of Section 5.5.3).¶
The IdP compares both iss and sub with the canonical actor identity
of the authenticated client (Section 5.5.2).¶
cnf:REQUIRED. A confirmation claim [RFC7800] binding the assertion to a key
the current actor proves. cnf MUST contain exactly one confirmation
method: jkt, the JWK SHA-256 thumbprint [RFC7638] of the DPoP key
[RFC9449] (Section 9.1).¶
iat, exp:REQUIRED. exp MUST follow iat. The assertion is short-lived: a CAI
SHOULD NOT issue a lifetime (exp - iat) longer than 300 seconds, and an
IdP SHOULD accept lifetimes of up to 300 seconds. The IdP rejects a lifetime
longer than the maximum it accepts (Section 5.5.3).¶
nbf:jti:REQUIRED. A replay-detection identifier that MUST be unique per iss
during the assertion validity window, with negligible probability of
collision. It SHOULD contain at least 128 bits of entropy.¶
The assertion MUST NOT contain:¶
a top-level sub, auth_time, acr, amr, or sid claim; or¶
the Token Exchange request parameters audience, resource, scope,
authorization_details, or requested_token_type (these are supplied by
the request).¶
The assertion occupies the subject_token role of Token Exchange [RFC8693]:
it carries no user subject, and the IdP resolves the user from the referenced
hop. It is not an [RFC7523] JWT-profile assertion.¶
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 generates a fresh handle for each hop and
includes it in that hop's ID-JAG. Each continuation creates a child hop with
its own handle and a reference to its parent. Every onward ID-JAG includes a
handle because the IdP does not know whether the target will continue the
chain. Section 10 describes the resulting correlation exposure.¶
A handle is non-secret but security-sensitive correlation state: it confers no authority by itself, yet a handle together with a CAI trust path and the actor's credential is a larger compromise than the actor's credential alone (Section 9.1); Section 5.3 limits where it travels.¶
The following rules apply:¶
When it establishes or continues a chain (Section 5.1), 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.¶
identity_continuation_handle MUST provide at least 128 bits of
unpredictability, MUST NOT contain user-identifying information, and MUST
consist of characters drawn from the base64url alphabet (A-Z, a-z,
0-9, -, _); it SHOULD NOT exceed 256 characters.¶
Across a trust boundary, the handle is accepted only inside an ID-JAG (by the RAS) or an Identity Continuation Assertion (by the IdP), never as a standalone value. Inside the accepting RAS's domain, the CAI obtains it from RAS state or a carrier (Section 5.3); afterward it travels in the assertion.¶
A continuation-aware RAS binds the handle to the authorization state it establishes (Section 5.2), and a hop is continuable only after that acceptance and binding (Section 5.2.1). RASes, Resource Servers, and CAIs MUST NOT modify the value. The IdP MUST use the handle only to resolve hop state, subject, and policy, never as authority.¶
This section specifies processing in protocol order: root exchange, RAS acceptance, handle propagation, assertion issuance, and continuation exchange (Section 1.1). The following table locates each role's requirements.¶
| Role | Requirements |
|---|---|
| IdP | Establishing a Chain (Section 5.1), Continuation Exchange (Section 5.5), Chain Lifetime and Revocation (Section 6), IdP metadata (Section 7.1), Issuer Trust Configuration (Section 7.3) |
| Continuation-aware RAS | RAS Processing (Section 5.2), Handle Carriers (Section 5.3), RAS metadata (Section 7.2) |
| CAI | the assertion it issues (Section 3.1, Section 3.2), Assertion Issuance and its subsections (Section 5.4), Handle Carriers (Section 5.3), Separate CAI (Section 5.4.6) |
| Continuing workload | Assertion Issuance Request, Client Authentication, and Successful Response (Section 5.4.1, Section 5.4.2, Section 5.4.4); Continuation Request and Client Authentication (Section 5.5.1, Section 5.5.2); Successful Response (Section 5.5.4) and Error Response and Recovery (Section 5.5.6) |
A chain begins when the IdP issues a continuation-capable ID-JAG on a root exchange.¶
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 to issue a continuation-capable ID-JAG. The request presents a subject token supported by the base profile, 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 grant_type=urn:ietf:params:oauth:grant-type:token-exchange &requested_token_type=urn:ietf:params:oauth:token-type:id-jag &audience=https://ras.gateway.example/ &resource=https://gateway.example/ &scope=tools.invoke &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>¶
Processing an actor_token on the root exchange is outside the scope of both
this document and the base profile
([I-D.ietf-oauth-identity-assertion-authz-grant], Section 4.3.3), and the
root actor is the authenticated client (Section 5.1.3).¶
The IdP MUST establish a chain when tenant policy permits continuation for
the root exchange and the root subject token resolves to a
lifecycle anchor (Section 6.1). To
establish a chain, the IdP MUST include the root handle in the ID-JAG. Absent
permission to continue, the IdP MUST NOT establish a chain or include an
identity_continuation_handle.¶
Tenant policy may also restrict establishment to particular clients, grants, or targets.¶
The lifecycle anchor is the user's active IdP session or, for a durable chain, a refresh token's OAuth grant. When the subject token resolves to no anchor, the IdP issues the ID-JAG without a handle, as under the base profile. No request parameter requests chain establishment. Advertised support (Section 7.1) indicates capability and does not authorize establishment.¶
For every hop it creates, root or child, the IdP MUST record the RAS audience placed in the ID-JAG; the issuers it trusts to attest that RAS's hops are read from current tenant configuration at each exchange (Section 7.3).¶
Establishment is at-least-once. Retrying a lost response MAY create a second chain, and the limits of Section 6.3 apply across every chain rooted in one chain authorization.¶
The root actor is the authenticated OAuth client, identified by the mapping in Section 5.5.2. The base profile's recommendation to use a confidential client applies to a root exchange that establishes a chain.¶
This document places no proof-of-possession requirement on the root exchange. The root client's obligations are those of the base profile, and sender constraint becomes a requirement for an actor that continues (Section 5.5.2).¶
A continuation-aware RAS implements this extension and advertises the continuation grant profile (Section 7.2). A RAS that does not implement it processes an ordinary ID-JAG and ignores the handle. An ID-JAG that such a RAS accepts cannot become a continuation source.¶
On accepting a continuation-capable ID-JAG, a continuation-aware RAS MUST:¶
accept the ID-JAG per [I-D.ietf-oauth-identity-assertion-authz-grant];¶
bind identity_continuation_handle, the ID-JAG's issuer, the tenant
where the deployment conveys one, and any confirmed key to the
authorization state it establishes, and record whether the authorization
is eligible for continuation under the RAS's own policy; and¶
when the ID-JAG carries cnf, as every onward ID-JAG does
(Section 5.5.5) and a root ID-JAG may, issue the access token bound to
the confirmed key with token_type DPoP ([RFC9449], Section 5), never
as a bearer token.¶
How the RAS receives or determines the tenant is deployment-specific; this document defines no tenant claim.¶
Under the base profile, the RAS validates the grant, authenticates the client,
applies local authorization policy, and issues an access token. An ID-JAG
carrying cnf uses the DPoP-bound JWT grant, which verifies its sender
constraint (Section 5.5.5). When a root ID-JAG lacks
cnf (Section 5.1), the RAS's own policy decides whether that
access token is sender-constrained.¶
Three rules govern binding the handle:¶
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.¶
All successful redemptions of one continuation-capable ID-JAG, identified by its validated issuer and handle and qualified by the tenant binding where the RAS records one, MUST resolve to the same hop binding, so that no redemption creates a distinct continuation source. A matching handle under another issuer is a different grant, not a retry.¶
The RAS exposes the binding, its record linking the handle to authorization state, only within its trust domain; the handle itself travels in the access token or another carrier as Section 5.3 describes.¶
Two parties hold facts about a hop, neither carried on the wire:¶
| Fact | Held by | Meaning |
|---|---|---|
| Issuance | IdP | The IdP issued the ID-JAG and recorded the hop. |
| Acceptance | RAS | The RAS accepted the ID-JAG and bound the handle to the resulting authorization. |
The IdP learns of acceptance through a trusted CAI's attestation and maintains no synchronized acceptance state (Section 1.1). The CAI attests only hops the RAS has accepted. A RAS acting as CAI attests its own hops; a separate CAI confirms acceptance and activity according to the RAS's authorization semantics (Section 5.4).¶
An issued hop cannot be continued without a trusted CAI's attestation. Unless the CAI is compromised (Section 9.4), no trusted CAI attests an issued-but-rejected ID-JAG, so continuation fails closed.¶
A hop is continuable while a CAI trusted for its RAS can attest it as accepted and still active, and neither it nor any ancestor is revoked; whether a particular continuation from it succeeds is decided by the validation rules of the continuation exchange (Section 5.5.3).¶
RAS acceptance and recorded eligibility (Section 5.2) are prerequisites for CAI issuance. The IdP authorizes downstream access under the chain authorization and current policy (Section 5.5.3).¶
Each call includes a credential or context that identifies exactly one RAS-bound authorization. The CAI MUST use the handle bound to that authorization, whether it reads RAS state directly or receives a carrier derived from it. A requester chooses which credential to present, but cannot supply or override its handle separately. A session or subject alone is not enough to select the authorization: doing so could attach another user's handle to the call.¶
When the RAS also acts as CAI, it reads the handle from its authorization state. It may also include the handle in its access token (Appendix B.1). A separate CAI receives the handle through a carrier derived from the RAS binding (Section 5.2). A Transaction Token [I-D.ietf-oauth-transaction-tokens] is one such carrier, an optional intra-domain choice and not a dependency of this profile. The carrier is accepted only within that trust domain (Section 5.4).¶
The source of the authorization context depends on the type of call:¶
For an ingress call, the access token, after the resource verifies its proof of possession where the token is sender-constrained.¶
For a downstream call, a carrier forwarded from that ingress.¶
For a scheduled run, the task named by an authenticated actor, resolved to the task authorization for which that actor is designated. The CAI MUST derive a scheduled continuation from durable RAS task authorization, not from a scheduler-held handle (Section 9.2). 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 trusted CAI.¶
The CAI checks acceptance freshness for every carrier (Section 5.4.3).¶
A Resource Server has no obligations under this document. A carrier SHOULD NOT expose the handle to a party with no role in continuation. Deployments keep this non-secret but security-sensitive correlation state (Section 4) out of logs, traces, and responses.¶
The CAI issues the Identity Continuation Assertion that a workload presents to
continue a chain across a boundary. The CAI MUST set the assertion's aud to
the IdP recorded in the hop's RAS binding (Section 5.2). The CAI
MUST NOT accept an IdP audience supplied by the requester.¶
The CAI attests three facts about its own domain:¶
The RAS accepted the ID-JAG for the hop.¶
The authorization the RAS established from it is active and, by the RAS's own authorization semantics, eligible for continuation.¶
The authenticated workload is associated with that authorization context, in which it received, or was designated to process, the request that hop authorized.¶
Whether that actor may continue, and to what, is the IdP's decision under the chain authorization and current policy (Section 5.5.3).¶
A CAI that is an OAuth authorization server, including a RAS acting as its own CAI, MAY issue assertions from its token endpoint using Token Exchange [RFC8693] as profiled in this and the following subsections. Such a RAS SHOULD support this method of issuance, so that a workload in its domain has one request to implement. Issuance by other means remains deployment-specific (Section 5.3).¶
A workload obtains assertions from the token endpoint of the CAI its deployment designates. For a RAS acting as its own CAI, this is the RAS's token endpoint, discoverable through the RAS's metadata (Section 7.2); a separate CAI is configured within the trust domain, which this document leaves to the deployment (Section 5.3).¶
The current actor acts as an OAuth client of the CAI. It makes a Token Exchange request to the CAI's token endpoint with the following parameters:¶
grant_type:REQUIRED. The value urn:ietf:params:oauth:grant-type:token-exchange.¶
requested_token_type:REQUIRED. The value
urn:ietf:params:oauth:token-type:identity-continuation.¶
subject_token:REQUIRED. Either the access token the client received on the call it is continuing, or the Transaction Token that carries the hop's handle for that call.¶
subject_token_type:REQUIRED. urn:ietf:params:oauth:token-type:access_token or
urn:ietf:params:oauth:token-type:txn_token
[I-D.ietf-oauth-transaction-tokens], matching the subject_token.¶
The audience, resource, scope, actor_token, and actor_token_type
parameters MUST NOT be included: targets and scope are chosen at the IdP
exchange (Section 3.2), and the authenticated client is the actor
named in act.¶
The following example carries the client's authentication and its DPoP proof (Section 5.4.2):¶
POST /token HTTP/1.1 Host: cai.example Content-Type: application/x-www-form-urlencoded DPoP: <proof of possession of the key to be placed in cnf> grant_type=urn:ietf:params:oauth:grant-type:token-exchange &requested_token_type=urn:ietf:params:oauth:token-type:identity-continuation &subject_token=<access token presented to the client> &subject_token_type=urn:ietf:params:oauth:token-type:access_token &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion=<JWT>¶
The client MUST authenticate to the CAI's token endpoint ([RFC6749], Section
2.3). The client MUST include in the request a DPoP proof [RFC9449] of the
client's own key. The CAI MUST verify that proof and bind the assertion to the
proven key in cnf. The CAI's verification is defense in depth: the actor
proves possession of that key again to the IdP at the continuation exchange,
which is the security boundary for continuation (Section 5.5.2).¶
The CAI MUST issue only for an actor it is authoritative to associate with the RAS-accepted authorization, typically one in the RAS's trust domain; actor authentication is deployment-specific.¶
The CAI MUST verify that the subject_token is of the type its
subject_token_type declares (Section 5.4.1) and is one of the
following:¶
an access token the accepting RAS issued, that RAS being one whose hops the CAI attests, unexpired, and valid for a protected resource that the authenticated client operates, as determined from the CAI's registration or configuration of that client; or¶
a Transaction Token valid for the CAI's trust domain under
[I-D.ietf-oauth-transaction-tokens], Section 12.2, carrying the typ
header and issuer that specification defines, and carrying the hop's handle
as chain context (Section 5.3, Section 5.4.6).¶
A token of another type presented as subject_token, such as an ID-JAG or an
Identity Continuation Assertion, is unacceptable (Section 5.4.5).¶
With an access token, the client is a resource server exchanging a token it
received, the scenario of the example in [RFC8693], Section 2.3. A RAS acting
as CAI resolves its own token; a separate CAI resolves it as Section 5.4.6
describes. In this exchange the subject_token identifies the RAS
authorization context from which continuation is requested; its subject is not
copied into the resulting assertion.¶
Either subject_token type supplies the facts below. The CAI MUST
authenticate the actor and issue only after establishing these facts:¶
The handle came through an authenticated, confidential, integrity-protected channel or equivalent authenticated state.¶
The current actor controls the key placed in cnf.¶
act names that actor. If offline attenuation reached the actor, the
attenuated credential it received, the subject_token or the carrier that
conveyed it, is valid (Appendix A.1).¶
The handle is the one bound to the authorization context the actor presents, its access token or carrier, and the actor is a party that context was issued or forwarded to (Section 5.3, Section 5.2.1). Selecting another intact context is the actor's choice; substituting a handle within a context is not. The handle is input to verify, never authority in itself (Section 4).¶
Evidence that is authoritative by the RAS's own authorization semantics, whatever the carrier, confirms that the authorization remains active and that its binding still records it as eligible for continuation. That evidence is either a recheck of RAS authorization state or, where the RAS's authorization is a self-contained short-lived token the RAS itself issued, that token's validity (Section 5.4.6).¶
The subject token's integrity protection and the authenticated request establish fact 1; the DPoP proof, fact 2; client authentication, fact 3, whose attenuation condition the CAI establishes by validating the attenuated credential it received; and the bound handle and the RAS's acceptance evidence, facts 4 and 5.¶
Section 9.1 traces the binding chain these facts form from ID-JAG to assertion.¶
A live recheck SHOULD be used where the tenant requires withdrawal of a hop's
authorization to stop fresh assertions before the RAS's token would expire.
With self-contained evidence, the CAI stops issuing when that token expires,
as for any OAuth access token. Where the evidence is a self-contained token,
the assertion's exp SHOULD NOT exceed that token's expiry, so that an
assertion is not presentable after the evidence that supported it has lapsed
(Section 6.2).¶
A domain may add its own conditions for issuing, for example limiting which of its workloads may obtain assertions, but such conditions narrow issuance only. Target or purpose hints can narrow CAI issuance, but neither they nor propagated context expand the authority the IdP may issue (Section 5.5.3).¶
A successful response is a Token Exchange response ([RFC8693], Section
2.2.1) in which access_token carries the Identity Continuation Assertion,
issued_token_type is
urn:ietf:params:oauth:token-type:identity-continuation, token_type is
N_A (not applicable), and expires_in reflects the assertion's lifetime.
The access_token member is the [RFC8693] response container; the assertion
is not an OAuth access token, which token_type N_A signals. This document
adds one parameter:¶
identity_continuation_authorization_server:REQUIRED. A JSON string containing the issuer identifier ([RFC8414]) of
the authorization server at which the client exchanges the assertion for an
ID-JAG. The CAI MUST set its value to the assertion's aud claim. The client
uses this parameter to identify the IdP without decoding the assertion.¶
The client obtains that IdP's token_endpoint from its authorization server
metadata ([RFC8414]), retrieved with the oauth-authorization-server
well-known URI suffix under the issuer identifier. Before sending the assertion
or its own credentials there, the client MUST confirm that the returned issuer
exactly matches identity_continuation_authorization_server. Where the IdP
publishes no metadata, the client uses configuration bound to that issuer
identifier (Section 7).¶
The client SHOULD present the assertion only to an IdP it is configured to trust; this parameter identifies the destination but does not establish trust.¶
The CAI MUST NOT include a refresh_token in the response, which would let a
client obtain further assertions without presenting a token or passing the
acceptance check.¶
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:identity-continuation",
"access_token": "<Identity Continuation Assertion, compact JWS>",
"token_type": "N_A",
"identity_continuation_authorization_server": "https://idp.example/",
"expires_in": 180
}
¶
On failure, the CAI returns an error response according to [RFC6749], Section 5.2, and [RFC8693], Section 2.2.2. This document specifies the following error mappings:¶
invalid_request when the subject_token is invalid or unacceptable under
policy, including when it is unknown, expired, revoked, not of the type its
subject_token_type declares, not valid for a resource the client operates,
has no bound handle, or names an authorization whose binding does not record
it as eligible for continuation, or when the request includes a parameter
this document prohibits (Section 5.4.1);¶
unauthorized_client when the client is not permitted to use this grant
type; and¶
DPoP nonce processing and the use_dpop_nonce error apply unchanged from
[RFC9449].¶
A separate CAI MUST obtain the handle and evidence of RAS acceptance and continued eligibility from a source authoritative for that RAS within its domain (Section 8.1):¶
For an access-token subject_token (Section 5.4.1), the CAI
uses introspection or validates a self-contained token.¶
The handle alone does not convey the originating IdP, tenant, or eligibility
for continuation. The introspection response's active member reports token
activity, not continuation eligibility ([RFC7662], Section 2.2). A separate
CAI relying on introspection therefore obtains the eligibility and the
originating IdP and tenant from deployment-defined evidence or configuration,
as the requirement above already demands.¶
With a Transaction Token, the handle arrives through the carrier of Section 5.3. Validating that token alone does not establish RAS acceptance or eligibility for continuation.¶
The IdP accepts a separate CAI's attestation only where it trusts that CAI to attest the accepting RAS's hops, from tenant configuration (Section 7.3, the issuer-trust rule of Section 5.5.3).¶
A continuation exchange is an OAuth 2.0 Token Exchange request [RFC8693]
whose subject_token is an Identity Continuation Assertion. It uses the same
Token Exchange framework as the root exchange (Section 5.1),
substituting the assertion for the root credential and adding the actor
authentication and DPoP proof described below.¶
Before a chain can continue to a target, the current actor needs a client registration or resolvable client identity at that target's RAS (Section 5.5.5).¶
A continuation exchange presents an Identity Continuation Assertion, a DPoP
proof of the cnf key, and the actor's client authentication
(Section 5.5.2):¶
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 &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion=<JWT>¶
The request carries no actor_token or actor_token_type: the actor named in
act is the authenticated client (Section 5.5.2), and the IdP rejects a
request carrying either (Section 5.5.3, Section 5.5.6).¶
The continuation request, never the assertion, supplies the requested
audience, resource, scope, requested_token_type, and any
authorization_details [RFC9396] (Section 3.2). A request can carry
multiple resource indicators [RFC8707]. The authorization rule
(Section 5.5.3) applies to authorization_details and to scope alike.¶
The current actor MUST authenticate as an OAuth client with a credential that
resolves to its canonical actor identity. The canonical actor identity is the
(iss, sub) pair the IdP holds for that client.¶
The iss member identifies the actor's identity authority, which determines
issuer pairing (Section 5.5.3, Section 9.4) independently of the
credential parameter. If the client registration specifies no other canonical
actor identity, iss is the IdP's issuer identifier and sub is the
client_id. This applies, for example, to a client registered without a
workload identity that authenticates with a client secret or mutual-TLS
certificate.¶
The IdP MUST derive that pair from its registration of the client or its configuration of the client's credential issuer, not from the client authentication method, and MUST NOT accept a self-asserted mapping.¶
For [RFC7523] client authentication, the IdP MUST authorize the assertion's issuer for the authenticated client. The canonical actor identity remains the pair derived from the IdP's configuration (Appendix A.4).¶
This mapping applies to every exchange. Client authentication identifies the
root actor; this profile adds no proof-of-possession requirement to the root
exchange (Section 5.1). On a continuation exchange (Section 5.5.1),
the IdP MUST also match the canonical actor identity to the assertion's act.¶
The comparison runs between actor identities, never between a raw OAuth client
identifier and an act value:¶
authenticated OAuth client
| authoritative mapping (IdP registration)
v
canonical actor identity (iss, sub)
^
| equal
act
¶
The IdP MUST compare the actor iss and sub as case-sensitive strings with
no transformation or canonicalization ([RFC7519]): the assertion's act is
compared with the canonical actor identity, and identities in different tenants
never compare equal.¶
The actor MUST prove possession of the key in cnf; for the jkt method, that
proof is a DPoP proof [RFC9449]. The IdP MUST bind the onward ID-JAG to a key
the actor proves in the request. This version defines only DPoP confirmation;
the target validates it using [RFC9449]. Support for mutual-TLS confirmation
[RFC8705] remains an open question (Appendix C).¶
A request contains one DPoP proof, so the assertion and resulting ID-JAG are bound to the same key. Client authentication may use an independent credential. Other confirmation methods could support different keys (Appendix C). Key rotation takes effect when the actor obtains an assertion bound to the new key.¶
Where the IdP offers idempotent retry, a presentation whose (iss, jti)
matches an ISSUED reservation, as Section 5.5.7.1 defines, is processed
under that section; an IdP that does not offer retry rejects every second
presentation (Section 5.5.7). For a first presentation, the IdP MUST
reject the request unless every rule below holds. Section 5.5.6 specifies
error precedence when multiple rules fail.¶
Request parameters.¶
exactly one each of grant_type, subject_token, subject_token_type,
requested_token_type, and audience, and no actor_token or
actor_token_type ([RFC8693], Section 2.1);¶
zero or more resource, treated as an order-independent set, and at
most one each of scope and authorization_details, all OPTIONAL and,
when present, evaluated by the authorization rule; 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 is a JWS in Compact Serialization, and is neither a JWE nor a nested JWT (Section 3.1);¶
it carries 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, exp, and any nbf are NumericDate
numbers;¶
act carries none of the members Section 3.2 forbids (exp,
nbf, aud, scope, cnf, and a nested act);¶
aud exactly matches the IdP's issuer identifier;¶
the signature validates with the issuer's resolved signing keys (Section 7);¶
alg is neither none nor a symmetric algorithm and is on the IdP's
allowlist (Section 9.7, [RFC8725]); and¶
the signing key comes from trusted issuer configuration, not from a
jku, x5u, or embedded jwk header, though kid MAY select among
the configured keys;¶
Issuer trust.¶
the assertion iss is either the accepting RAS itself, identified by the
ID-JAG aud recorded for the hop as a string or one-element array, or
another issuer the IdP trusts to attest that RAS's hops, recorded from
tenant configuration (Section 7.3); and¶
in either case, that issuer is trusted for the chain's tenant, recorded at establishment, and authorized to pair, for that tenant, with the actor's identity authority (Section 5.5.2);¶
Chain state.¶
the handle identifies a hop the IdP issued, on an active chain, that the assertion attests as accepted: a valid assertion from a CAI the IdP trusts for that hop's RAS is itself that attestation; no claim in the assertion carries it (Section 5.2.1);¶
neither the presented hop nor any ancestor is revoked;¶
the actor lineage that results from merging consecutive same-actor
entries, as the onward act will (Section 5.5.5), is within its
actor-lineage depth bound, which counts lineage entries, not hops; and¶
the continuation is within the fan-out, rate, and hop-count limits of the chain authorization (Section 6.3);¶
Current actor and binding.¶
act is present, conforms to the schema of Section 3.2, and
identifies the current actor, the canonical actor identity of the
authenticated client (Section 5.5.2);¶
the request proves possession of the cnf key with a matching DPoP
proof (Section 5.5.2, [RFC9449]); the assertion is never
accepted as a bearer token ([RFC7800]); and¶
the IdP can resolve, for the requested audience, both the user's subject
identifier and the actor's client identifier (Section 5.5.5);¶
Freshness and replay.¶
iat is within the IdP's permitted clock skew, exp follows iat, the
assertion is unexpired within that same skew, and any nbf has passed
within it ([RFC7519], Section 4.1.5). One skew value applies to iat as
future skew, to exp as past skew, and to reservation retention
(Section 5.5.7); it SHOULD NOT exceed 60 seconds;¶
the assertion's lifetime does not exceed the maximum the IdP accepts (Section 3.2); and¶
jti is not yet reserved for the assertion issuer (Section 5.5.7);
where the IdP offers idempotent retry, a presentation matching an ISSUED
reservation is recovery (Section 5.5.7.1), not a continuation
exchange, and any other reserved jti is rejected;¶
Authorization. The IdP MUST issue an ID-JAG only if the chain
authorization associated with the referenced hop (Section 5.1.4)
and current policy permit the authenticated actor to continue from that hop
and permit the authority represented by the resulting ID-JAG. This
evaluation includes the audience, resources, scopes, and authorization
details, including any default scope or other defaults the IdP applies
under its policy. An omitted scope uses a policy default or results in
invalid_scope ([RFC6749], Section 3.3). An omitted resource does not
by itself require a default. The IdP MUST reject the
request if it cannot establish that authorization.¶
The issued ID-JAG carries the scope, resource, and
authorization_details values that express the granted authority.
Target or purpose hints reaching the CAI (Section 5.4.3)
MUST NOT control the IdP's target decision, and propagated context MUST NOT
expand the authority permitted by the chain authorization and current
policy. The IdP evaluates each authorization detail according to its type
([RFC9396]) and rejects a type whose authorization semantics it does not
implement.¶
The response to a continuation exchange follows the base ID-JAG profile: the
IdP returns the ID-JAG in access_token, with token_type N_A (not
applicable; [RFC8693], Section 2.2.1). The IdP MUST NOT include a
refresh_token: a renewable credential would let the workload obtain further
grants without fresh CAI attestation, or root a new chain through the
refresh-token anchor, outside the hop's revocation dependencies.¶
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
}
¶
On success, the IdP MUST record a child hop (Section 5.2.1) of the
presented hop before returning an ID-JAG carrying the resolved target sub
and the child hop's fresh handle. An idempotent retry (the freshness rule;
Section 5.5.7.1) instead returns the previously issued grant unchanged,
creating no new hop or handle.¶
The hop reference is delivered as the ID-JAG's identity_continuation_handle
claim (Section 4), a claim inside access_token and not a separate Token
Exchange response parameter; the accepting RAS binds it (Section 5.2),
and the CAI reaches it through RAS state or intra-domain context
(Section 5.3).¶
There is no chain-expiry response parameter: chain lifetime is authoritative at the IdP (Section 6), and a deployment needing advance warning conveys it through task or authorization state, an optional ID-JAG claim, or a management API.¶
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 subject identifier for the target
audience, and aud_sub remains available under the base profile where the
target's native subject namespace differs.¶
Because the onward ID-JAG carries cnf, the actor redeems it with the
DPoP-bound JWT grant, urn:ietf:params:oauth:grant-type:jwt-dpop
([I-D.parecki-oauth-jwt-dpop-grant]), and a DPoP proof of the bound key, as
the base profile specifies for a key-bound ID-JAG
([I-D.ietf-oauth-identity-assertion-authz-grant], Section 9.8.1.2.1). The
target RAS needs no continuation support to redeem this grant. A RAS that
does not implement this profile may issue a bearer access token under the
base profile. A continuation-aware RAS follows the binding requirements of
Section 5.2, even if no further continuation occurs.¶
Where the recorded root authentication context contains auth_time, acr, or
amr, the IdP MUST include them in the onward ID-JAG unchanged. Continuation
MUST NOT extend or strengthen the authentication context, for example by
raising acr or adding amr beyond the user's root authentication.¶
The IdP constructs act as follows:¶
It places the authenticated current actor atop the presented hop's lineage,
whose origin is the authenticated client of the root exchange even if
the root ID-JAG carries no act (Section 5.1.3); it never copies lineage
from the assertion, and siblings do not contribute.¶
A hop's parent reference is immutable. The IdP MUST derive lineage from that hop's ancestry to the root, excluding sibling branches. Storage and traversal methods are implementation-specific.¶
Consecutive identical actors, those whose canonical actor identities are
equal under the comparison rules of Section 5.5.2, both iss and
sub, merge into one entry, though the hop record
remains; policy MAY limit disclosed depth, narrowing what a target sees
without changing the actor-lineage depth bound the IdP enforces (the
chain-state rule of Section 5.5.3).¶
A RAS MUST NOT read the absence of a further nested act as proof that no
earlier actor exists, because policy may narrow what is disclosed: act is
the disclosed actor lineage, not the authoritative history, which only the
IdP's hop records hold.¶
The onward ID-JAG's client_id identifies the current actor's OAuth client
at the target RAS and may differ from its client identifier at the IdP.
If the IdP cannot resolve that target client identity, the request fails
with invalid_target (Section 5.5.6).¶
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"
}
¶
On failure, the IdP returns an error response ([RFC6749], Section 5.2;
[RFC8693], Section 2.2.2). On a first presentation, when more than one rule
of Section 5.5.3 fails, the IdP MUST return the code for the earliest
failure. Request-parameter and well-formedness failures (including signature
verification) come first. No chain-state code is returned before issuer trust
for the hop's RAS is established, nor before the client authentication and key
proof of the current-actor rule succeed, so no chain-state code reaches a
caller that has not authenticated as the current actor and proved the cnf
key. Among chain-state failures a permanently unusable hop precedes a limit.
The contents of an unverified assertion never determine the response, and a
missing hop is invalid_request, so a caller learns nothing about which
handles exist.¶
For recovery, client authentication, key proof, and fingerprint matching precede chain-state checks (Section 5.5.7.1).¶
On a first presentation or matching ISSUED recovery, the IdP MUST return
invalid_continuation (Section 11) when the handle identifies an issued hop
that is permanently unusable: its chain has expired or ended,
the hop or an ancestor is revoked, or the tenant has withdrawn the chain's
permission to continue (Section 6.2). The IdP MUST NOT return
invalid_continuation in any other case.¶
For other failures, the IdP MUST use the following error codes:¶
invalid_request: a malformed, inconsistent, or unacceptable token, including
an assertion that fails the well-formedness rule of Section 5.5.3 (which
covers signature verification) or the issuer-trust rule; an act that does
not equal the authenticated client's canonical actor identity
(Section 5.5.2); an unknown handle; a
lifetime above the accepted maximum; prohibited actor_token or
actor_token_type parameters; or a reserved assertion that cannot be
processed as idempotent recovery (Section 5.5.7.1), other than a
matching ISSUED recovery whose hop is permanently unusable, which is
invalid_continuation.¶
invalid_dpop_proof: a DPoP failure.¶
unauthorized_client: the chain authorization or current policy does
not permit this actor to continue from the hop. Other actors may still
continue; withdrawal of the chain's permission is invalid_continuation.¶
invalid_grant: continuation would exceed actor-lineage depth, fan-out,
hop-count, or rate limits (Section 6.3). A rate-limited request can
be retried after the policy-defined window; other limit failures require a
different request.¶
invalid_target: the requested audience or a requested resource is not
permitted by the chain authorization or current policy, or the IdP cannot
resolve the user's subject identifier or the actor's client identifier at
the target.¶
invalid_scope: a requested scope is not permitted by the chain
authorization or current policy, or scope is omitted and no policy
default exists ([RFC6749], Section 3.3).¶
invalid_authorization_details: a requested authorization detail is not
permitted by the chain authorization or current policy, or its type is one
the IdP does not implement.¶
DPoP nonce processing and the use_dpop_nonce error apply unchanged from
[RFC9449].¶
Recovery from invalid_continuation requires a new root exchange, authorized
under current policy and resolving to an active anchor
(Section 5.1). Establishing a new chain does not reactivate the old
chain's handles.¶
Other errors apply to the request and do not establish that the chain is permanently unusable. An actor-lineage depth rejection concerns the particular continuation whose resulting lineage would exceed the bound, not the hop itself; a continuation that merges into an existing lineage entry, or a later policy raising the bound, may still succeed.¶
After a lost response, the client MAY retry the same assertion where the IdP offers idempotent retry (Section 5.5.7.1), or obtain a fresh assertion. Expired acceptance evidence can prevent fresh assertion issuance; shared fan-out or hop-count limits can prevent a subsequent exchange (Section 6.3). A fresh assertion may create an equivalent grant and sibling hop but no additional authority.¶
A client cannot distinguish on the wire a presentation whose request does not
match its reservation's fingerprint from one whose reservation is RESERVED or
FAILED: all three return invalid_request (Section 5.5.7.1). The client
can retry the original request within the retry window, since pending issuance
may complete, or obtain a fresh assertion. Other invalid_request failures,
such as malformed requests or prohibited parameters, require correcting or
abandoning the request.¶
The IdP MUST issue at most one grant per assertion, including under concurrent
presentations: it reserves the assertion's (iss, jti) as an atomic
first-writer decision once validation succeeds and before it issues the grant,
and MUST retain the reservation through exp plus the permitted clock skew
(one value, per the freshness rule of Section 5.5.3). Uniqueness is keyed on
(iss, jti), since partitioning by tenant alone would let two assertion
issuers in one tenant collide on a reused jti. The reservation MUST be
visible to every IdP instance that accepts assertions for that issuer, and an
instance that cannot reach that shared state MUST reject the request rather
than issue. Without idempotent retry this needs only the set of (iss, jti)
values presented within that window.¶
A request that fails validation creates no reservation and does not modify any existing reservation.¶
The IdP MUST reject a second presentation of a reserved assertion unless it offers idempotent retry.¶
A consumed assertion is not equivalent to a fresh one. With live acceptance evidence, the CAI refuses a fresh assertion once the hop's authorization has lapsed (Section 5.4.3); with self-contained evidence, issuance continues until the RAS token expires, and single-use still keeps each consumed assertion from authorizing more than its one grant within that tail (Section 9.1). Single-use therefore bounds staleness and multiplicity; it does not itself establish that the hop was accepted.¶
An IdP MAY offer idempotent retry by binding the reservation to a fingerprint
of the request first authorized and recording the reservation as RESERVED,
ISSUED, or FAILED (distinct from the hop facts of Section 5.2.1). These
names describe the states an IdP distinguishes, not a required representation.
The IdP MUST reject a presentation whose (iss, jti) matches a reservation
but whose request does not match that reservation's fingerprint. A presentation
matching the fingerprint of an ISSUED reservation within the IdP's retry window
is processed as recovery, defined below. 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 confirmation key in cnf (its jkt thumbprint in this version); and¶
a SHA-256 hash of the exact subject_token after form decoding, which binds
the fingerprint to the specific assertion and its handle.¶
An IdP that offers idempotent retry MUST retain the ISSUED reservation, its fingerprint, and its result for a retry window it defines, alongside the reservation retention of Section 5.5.7; recovery is available only within that window and only while the issued grant is unexpired. The window is a deployment choice and MAY be advertised or documented.¶
For recovery, the IdP MUST verify:¶
the requester's client authentication and canonical actor identity;¶
the DPoP proof of the cnf key;¶
the request fingerprint; and¶
that the presented hop and every ancestor remain unrevoked and the chain has not ended, including through anchor expiry or withdrawal of the chain's permission to continue (Section 6.2).¶
If these checks succeed and the issued grant is unexpired, the IdP MUST return
that grant unchanged. Otherwise, it MUST reject the request under
Section 5.5.6. The IdP never extends or reissues the grant; after its
exp, the client obtains a fresh assertion.¶
Recovery applies no other validation or policy checks. Changes to actor or target authorization, or to issuer trust, do not prevent recovery of an already-issued grant. Recovery creates no hop and consumes no fan-out, hop-count, or rate budget. Throttling repeated requests is a deployment choice.¶
A presentation matching a RESERVED reservation, whose first presentation has
not completed, MUST be rejected with invalid_request; the client can retry
it once that first presentation completes (Section 5.5.6). A reservation
that does not reach ISSUED before exp plus the permitted clock skew becomes
FAILED, which is final: a presentation matching a FAILED reservation MUST be
rejected with invalid_request, and the client obtains a fresh assertion.¶
Application idempotency is outside the scope of this document. Section 8 provides implementation guidance; discovery of optional recovery support remains an open question (Appendix C).¶
This profile supports session-anchored chains and, optionally, durable chains anchored to a refresh token's grant. A session-anchored chain ends with the session; a grant-anchored chain can support an unattended agent after logout (Appendix B.3). The ending rules and limits apply to both forms. A reader implementing session anchors only can skip the grant-specific processing and background example.¶
A chain is continuable only while active at the IdP. Each continuation is a fresh policy check, and revoking a hop stops its descendants at the next continuation, fail-closed; an offline-attenuated token, by contrast, stays usable for its lifetime without contacting an authority.¶
Revocation does not invalidate an already-issued ID-JAG, so the revocation window is the ID-JAG's remaining redemption window plus the lifetime of the access token a late redemption obtains; any refresh token the RAS issues, which the base profile recommends against, extends it further.¶
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; 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 root subject token resolves to one of these anchors (Section 5.1):¶
an ID Token sid [OIDC.FrontChannelLogout] resolving to an active IdP
session for that user and client;¶
a SAML SessionIndex [SAML2.Core] resolving to an active IdP session
for that user and client; or¶
for a durable chain, a refresh token's OAuth grant.¶
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.¶
Support for grant anchors is OPTIONAL; a session-anchored implementation is complete. Without it, base processing of a refresh-token subject is unchanged; any otherwise-authorized ID-JAG omits the continuation handle (Section 5.1.2). Rotation of a refresh token does not affect the grant anchor.¶
A chain ends when:¶
the session it is anchored to terminates;¶
the grant it is anchored to expires or is revoked; or¶
tenant policy withdraws permission to continue it.¶
A chain MUST NOT outlive its anchor (Section 6.1): a session-anchored chain ends with the session, and only a grant-anchored chain may outlive logout. An authorization that a session produced but that has its own lifecycle, such as a refresh token's grant, is a grant anchor, so logout ends a chain only when the session itself is the anchor. 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 has these duties over chain lifetime:¶
it MUST bound chain lifetime by the chain authorization;¶
it MUST support administrative revocation of an entire chain and MAY revoke an individual hop's subtree; and¶
it MUST reject continuation on a revoked, expired, or ended chain.¶
The IdP ends a chain when it observes the withdrawal, whether through a policy event or at the next continuation attempt, and an ended chain stays ended: restoring the policy that withdrew permission does not revive it, its handles remain permanently unusable (Section 5.5.6), and a new chain is needed.¶
RAS-local withdrawal stops fresh assertions once the CAI observes it; self-contained acceptance evidence may remain usable until its expiry (Section 5.4.3). Previously issued assertions remain subject to the IdP's current chain, trust, and authorization checks (Section 5.5.3). RAS-local withdrawal does not revoke descendants accepted at other RASes; stopping their continuation requires IdP revocation of the chain or affected subtree.¶
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.¶
Fan-out is the number of child hops continued from one hop and is counted per hop. Hop count is the total number of hops in a chain across all branches. Rate is the number of newly issued continuations, not attempts, within a window tenant policy defines. Hop count and rate are counted per chain and, where the tenant configures one budget for a chain authorization, aggregated across every chain rooted in it, so sibling chains share that budget; a retried establishment (Section 5.1) MUST NOT evade it. Revocation of the chain authorization applies to every chain rooted in it. The actor-lineage depth bound, set by tenant policy, is enforced per branch.¶
The IdP MUST enforce a finite hop-count limit on every chain, either the tenant's configured value or the IdP's default, so that a workload continuing as itself cannot extend a chain without bound. Fan-out and rate limits remain optional tenant controls. Whether the profile should fix a default is open (Appendix C).¶
An IdP and a Resource Authorization Server advertise support in their metadata; the IdP configures the CAIs and actor identity authorities it trusts.¶
An IdP that supports this profile SHOULD signal it in its authorization server metadata [RFC8414] with the following parameter:¶
identity_continuation_supported:OPTIONAL. Boolean, default false, indicating that the IdP accepts the
urn:ietf:params:oauth:token-type:identity-continuation subject token type
and issues continuation-capable ID-JAGs. Such an ID-JAG is still the
urn:ietf:params:oauth:token-type:id-jag type; an IdP that sets this flag
also lists that type in identity_chaining_requested_token_types_supported
([I-D.ietf-oauth-identity-chaining]). This flag adds only the continuation
capability.¶
A Resource Authorization Server that binds the
identity_continuation_handle claim to authorization state MUST advertise
support by listing the grant profile
urn:ietf:params:oauth:grant-profile:id-jag-continuation in
its authorization_grant_profiles_supported
[I-D.ietf-oauth-identity-assertion-authz-grant]. This value indicates that the
server recognizes continuation-capable ID-JAGs and performs that binding
(Section 5.2). The base ID-JAG grant profile indicates ordinary grant
processing without handle binding.¶
A Resource Authorization Server that advertises
urn:ietf:params:oauth:grant-profile:id-jag-continuation MUST also advertise
the base urn:ietf:params:oauth:grant-profile:id-jag profile and the
urn:ietf:params:oauth:grant-type:jwt-bearer grant type on which ID-JAG
depends ([I-D.ietf-oauth-identity-assertion-authz-grant]). It MUST also
advertise urn:ietf:params:oauth:grant-type:jwt-dpop, since every onward
ID-JAG it redeems carries cnf (Section 5.5.5).¶
The IdP MUST authorize CAI and actor identity authority pairings per tenant; separate trust in each is insufficient (Section 9.4). The IdP MUST scope CAI trust by issuer, keys, tenant, and the RAS it attests for. Tenant determination MUST derive from authenticated material, not requester-supplied input.¶
When a CAI's issuer identifier is that of an OAuth authorization server, the
IdP obtains its signing keys from the jwks_uri in that server's metadata
([RFC8414]); a CAI without such a jwks_uri, like any other CAI, uses
authenticated configuration. The IdP MUST refresh remotely obtained keys
under a bounded cache policy, so a key removed from the JWK Set stops
validating once the refresh takes effect.¶
The IdP evaluates issuer trust and keys against its current trusted issuer and key state, so removing an issuer or revoking its keys de-authorizes it for existing chains: continuation from such a CAI's assertions is refused at the next continuation exchange (the issuer-trust rule of Section 5.5.3).¶
CAI trust is provisioned and withdrawn through deployment-specific configuration at the IdP.¶
This non-normative section provides implementation guidance for IdPs, RASes, and CAIs. Conformance depends on the normative sections.¶
Continuation is per authorization context, not per call: a workload continues once to obtain an ID-JAG for a target and then reuses the access token it redeems there while that token remains valid and covers the requested access. Reuse requires the same user, tenant, actor, and key as the call that obtained the token. Each source hop needs its own continuation: reusing a sibling branch's token would associate subsequent continuation with that branch's lineage and revocation dependencies.¶
The first call requires assertion issuance, continuation exchange, and ID-JAG redemption. Later calls using the same valid token require no further exchanges.¶
Renewal is another continuation with a fresh assertion, which succeeds only if the CAI's preconditions still hold (Section 5.4.3); an expired access token does not by itself entitle the workload to another.¶
Every continuation depends on IdP availability. A separate CAI also needs access to the RAS's acceptance evidence. Offline attenuation can avoid these dependencies where the subject and issuer trust remain usable across the boundary (Appendix A.1); it cannot replace IdP subject resolution.¶
The IdP retains hop records for the chain's lifetime and prunes hop state for
ended chains and revoked hops. It retains each assertion's (iss, jti)
reservation as Section 5.5.7 requires, expiring it by the same clock
it uses to evaluate exp.¶
An IdP that offers idempotent retry also holds the ISSUED reservation, its fingerprint, and its result for the retry window it defines, with recovery governed by Section 5.5.7.1; an IdP that does not offer retry needs no more than the reservation itself. Ancestry caches and audit indexes can supplement immutable parent records, provided lineage and ancestor revocation checks remain correct for each branch (Section 5.5.5).¶
An IdP can derive handles from an internal delegation identifier using a keyed one-way function, provided the derived handles still satisfy rules 1 and 2 of Section 4 and remain unlinkable.¶
A RAS can make the handle binding and token issuance of Section 5.2 one outcome with a local transaction, or with a compensating action that revokes a token whose binding did not commit.¶
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.¶
Consecutive continuations by the same actor do not increase actor-lineage depth. The fan-out, rate, and hop-count limits of Section 6.3 bound this growth.¶
Failure paths worth testing before deployment:¶
Concurrent redemption of one ID-JAG.¶
A crash between issuing an ID-JAG and recording its hop.¶
A policy change between two continuations.¶
A credential from the wrong tenant.¶
Local revocation after an assertion has been issued.¶
An IdP can defer materializing chain state until the first continuation, provided the handle still resolves to the same root and chain authorization; deferral does not relax the replay rules of Section 5.5.7. Whether the hop tree could be replaced by self-verifying handles is an open question (Appendix C), not a realization this document describes.¶
The two topologies differ in which party performs the CAI role and how it obtains hop state:¶
| Topology | CAI role held by | Source | Fits when |
|---|---|---|---|
| Co-located | the accepting RAS | RAS state | one operator runs the domain |
| Separate | a separate CAI the IdP trusts for the RAS | domain carrier | the RAS is shared infrastructure, the gateway is only a Resource Server, or keys and audit need isolation |
In the baseline co-located deployment, the IdP configures the RAS's issuer trust (Section 7.3), including keys, tenant, and issuer pairings (Section 9.4). The RAS advertises the continuation profile (Section 7.2).¶
Both topologies produce the same Identity Continuation Assertion and apply the
same CAI requirements. Either CAI can issue the assertion from its token
endpoint (Section 5.4.1): a co-located RAS takes the access
token it issued as the subject_token, and a separate CAI takes the token
that carries the handle. Both require the same number of exchanges.
Co-location reduces configuration and trust relationships; a separate CAI
also requires a carrier and access to acceptance evidence.¶
Carrier selection is deployment-specific, subject to Section 5.3. The gateway example uses the RAS's access token (Appendix B.1). Common carriers include:¶
A Transaction Token [I-D.ietf-oauth-transaction-tokens] can carry the handle as request context (Section 5.3).¶
A signed JWT access token [RFC9068] issued by the accepting RAS can carry the handle as a claim. The claim records issuance-time state and therefore cannot reflect a later revocation; the CAI's acceptance check (Section 5.4.3) covers that.¶
For an opaque access token issued by the accepting RAS, its introspection
response [RFC7662] can carry the handle as a member, generated when
introspection occurs and present only when active is true.¶
This profile assumes TLS, correct IdP subject mapping and authorization evaluation, and the OAuth guidance of [RFC9700]. The adversaries considered are a party that captures an assertion or access token, a compromised or misdirected workload, a party holding a handle and nothing else, a rogue or over-scoped actor identity authority, a compromised RAS or CAI, an actor that forges lineage, and an attacker that substitutes token types, algorithms, or keys.¶
Section 10 addresses correlation and disclosure risks.¶
An Identity Continuation Assertion identifies the current actor. The IdP
matches its act claim to the authenticated client's canonical actor identity
(Section 5.5.2). An attacker cannot use a captured assertion without
also authenticating as that actor.¶
Sender constraint additionally requires possession of the cnf key, verified
with DPoP [RFC9449] (Section 5.5.3, Section 5.5.2). Possession of the
actor's client credential alone is insufficient without that key. The onward
ID-JAG and the continuation-aware RAS's access token are bound to the same key
(Section 5.5.2, Section 5.2). Sender constraint is maintained
across continuation-capable hops rather than demonstrated once at issuance,
with an authenticated transition to the next actor's key at each workload
boundary. Where the chain ends at a RAS that does not implement this profile,
that RAS's token binding is its own policy under the base profile
(Section 5.5.5).¶
The facts the CAI establishes before issuing (Section 5.4.3) form
the binding chain from ID-JAG to assertion. In the synchronous flow, the
redeemer proved the ID-JAG's cnf key at the RAS, which bound the handle to
its authorization state and the access token to that key (Section 5.2);
the caller proved that key on its call to the workload, which verified it as an
[RFC9449] resource server; the workload authenticates to the CAI and proves
its own key (facts 2 and 3); and the CAI places that key in cnf. Where the
context was forwarded within the domain, carried in a Transaction Token, or
derived from durable task state for a scheduled run (Section 5.3),
fact 4 substitutes for the call-boundary check.¶
The root hop is different. A root ID-JAG need not carry cnf, and its RAS
sender-constrains the access token by its own policy, so a root hop may issue
a bearer token. A party that captures such a token can call the workload and
so induce that honest workload's continuation under the workload's own key;
the workload's proof of possession does not prevent this, because the workload
is the legitimate presenter. Such a token weakens only the caller's link in the
binding chain; facts 2 and 3 apply unchanged.¶
Because acceptance does not bound downstream authority (Section 5.2.1), a captured root token can induce continuation to any target permitted by the chain authorization and current policy, not only the resource the token was issued for. Token theft is the inherited bearer-token risk; continuation can extend its consequences to the downstream authority available under that authorization. Sender constraint at ingress prevents use of the token without its bound key.¶
This profile adds no proof requirement at the root (Section 5.1); continuation rests on the continuing actor's key, the RAS binding, and the CAI's attestation, not on the root client's key. At assertion issuance the actor proves only its own key, not any key the incoming subject token is bound to (Section 5.4.1): the chain changes key at the call boundary by design, because the access token is the caller's credential and the assertion is the callee's, so the CAI does not compare the requester's proof with the access token's key.¶
Replay of a captured assertion is confined to the IdP continuation exchange and requires the actor's key. The freshness rule bounds the window, and single-use (Section 5.5.7) confines a consumed assertion to the one grant it first obtained: without it, an actor whose RAS-local authorization had lapsed, and whom the CAI would therefore refuse a fresh assertion, could keep continuing from a consumed one, to any target the chain authorization and current policy permit, until it expired. Idempotent recovery after a lost response is optional and does not reopen replay (Section 5.5.7).¶
A rogue or over-scoped actor identity authority is an impersonation vector: a party controlling one could name an actor in another domain or tenant and continue that actor's chains. The IdP therefore takes that authority from its own registration of the client or its configuration of the client's credential issuer, never from the request, and accepts it only where it is paired with the CAI for the chain's tenant (Section 5.5.2, Section 5.5.3).¶
An accompanying CAI assertion does not relax that. CAI attestation of the hop and authentication of the actor are independent checks, and neither substitutes for the other (Section 9.4).¶
A continuation requires all of these, and no one of them suffices alone:¶
a CAI the IdP trusts for the presented hop's accepting Resource Authorization Server, which attests the chain-to-actor transition (the issuer-trust rule of Section 5.5.3);¶
the actor's identity authority, trusted for the current actor's domain and tenant, which vouches for the actor through the mapping of its client credential (the current-actor rule of Section 5.5.3);¶
live proof of possession of the confirmed key (the current-actor rule of Section 5.5.3); and¶
the chain authorization and current policy enforced by the IdP (the authorization rule of Section 5.5.3).¶
The IdP's trust configuration records these pairings (Section 7.3).¶
Accepting the RAS's own identifier under the issuer-trust rule establishes no issuer, key, tenant, or issuer-pairing trust (Section 5.5.3, Section 9.4). Separating the CAI isolates keys and components but creates no protocol-level quorum: the IdP still sees one signed attestation (Section 8.1).¶
A workload that obtains the assertion by exchanging the access token or Transaction Token it holds for the call (Section 5.4.1) presents nothing it did not already hold; what it gains is the CAI's attestation, gated by policy and the acceptance check.¶
A compromised RAS can fabricate acceptance state in either topology, since a separate CAI reads that state as authoritative. A compromised separate CAI can additionally attest a hop the RAS refused. Section 6.2 describes the effects of RAS-local withdrawal, including delayed observation by a separate CAI. The chain authorization and current policy still bound the result.¶
The nested act claim is the disclosed actor lineage, a possibly minimized
representation of the IdP's hop lineage, which is the authoritative actor
history. 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 derives the onward lineage from its own
hop records (Section 5.5.5), and it rejects any mismatch between the current
actor and the assertion's act (the current-actor rule of Section 5.5.3).¶
A party therefore cannot rewrite history it does not control, and
offline-attenuation segments, which the IdP does not observe, do not enter
lineage. Lineage is disclosed, not exhaustive: policy may narrow it
(Section 5.5.5), so a rule such as "deny if a given actor ever
participated" cannot be enforced from act alone.¶
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 JOSE verification rules that deny this are stated in the well-formedness rule of Section 5.5.3.¶
A hop's identity_continuation_handle is visible to its ID-JAG client, the
accepting Resource Authorization Server, the CAI, the workload that receives
the assertion, and the IdP. Where a deployment propagates it, the carrier and
its recipients, including any audience of an access token that carries it,
see it as well (Section 5.3).¶
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, the disclosed actor lineage, also names the prior
actors to the accepting RAS outright, with no correlation needed; policy may
limit the depth disclosed to each audience (Section 5.5.5).¶
Note: The token type URI urn:ietf:params:oauth:token-type:id-jag referenced by
this document is registered by
[I-D.ietf-oauth-identity-assertion-authz-grant] and is not registered here.¶
Note: The authorization_grant_profiles_supported metadata parameter and the
base urn:ietf:params:oauth:grant-profile:id-jag value referenced by this
document are defined and registered by
[I-D.ietf-oauth-identity-assertion-authz-grant] and are not registered here;
this document registers only the
urn:ietf:params:oauth:grant-profile:id-jag-continuation value.¶
IANA is requested to register the following error in the "OAuth Extensions Error Registry" established by [RFC6749].¶
IANA is requested to register the following parameter in the "OAuth Parameters" registry established by [RFC6749].¶
identity_continuation_authorization_server¶
token response¶
IETF¶
This document, Section 5.4.4¶
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 5.2¶
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, Aaron Parecki¶
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 resolve the referenced hop and its chain state. 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, including an access token the accepting Resource Authorization Server issues (Section 5.3).¶
IETF¶
IANA is requested to register the following value in the "OAuth Token Introspection Response" registry established by [RFC7662].¶
identity_continuation_handle¶
The continuation handle bound to the introspected token's authorization,
present only when active is true; Section 5.3 constrains its
exposure within the issuing server's trust domain¶
IETF¶
This document, Section 5.3¶
This non-normative appendix records the principal design choices.¶
This profile serves deployments where the target trusts a common IdP to resolve the user's subject identifier. Each continuation returns to that IdP for subject resolution and checks of current authorization and chain state (Section 5.5.3). RAS-local withdrawal remains subject to the freshness of acceptance evidence (Section 6.2).¶
Offline attenuation, in which a party narrows and forwards a credential without contacting the IdP, such as [I-D.li-oauth-delegated-authorization], suits boundaries where existing subject and issuer trust remain usable and offline delegation is acceptable. A deployment can attenuate within a trust domain and continue across boundaries requiring IdP resolution. Crossing to a target that does not trust the common IdP requires a separate trust agreement and profile, such as [I-D.fletcher-transaction-token-chaining-profile].¶
An ID-JAG authorizes the client named in client_id to redeem the grant at
the RAS named in aud. When sender-constrained, it also binds that use to
the client's key. The RAS receives the grant as its audience; receipt does
not authorize the RAS to present it as a different client or replace its key
binding ([I-D.ietf-oauth-identity-assertion-authz-grant]).¶
Similarly, a workload receives an access token as the protected resource. The token authorizes the incoming caller's access and, when sender-constrained, is bound to that caller's key. Validating the caller's proof does not give the workload possession of the key or establish a credential for the workload to present at the IdP (Section 9.1).¶
The Identity Continuation Assertion makes this transition explicit. The CAI attests that the receiving workload is associated with an accepted, active authorization eligible for continuation, and issues an assertion addressed to the IdP and bound to that workload's key (Section 5.4.3). The IdP can then authenticate and authorize the continuing workload using its own identity and key (Section 5.5.3).¶
Direct exchange of either token under [RFC8693] would require additional rules establishing the receiving party's authority to continue and binding the exchange to its identity and key (Appendix C).¶
Assertion issuance reuses the Token Exchange [RFC8693] request and response framework at the CAI's token endpoint (Section 5.4.1).¶
A Transaction Token [I-D.ietf-oauth-transaction-tokens] may carry the context from which the CAI issues its assertion; its validity alone is not RAS acceptance evidence (Section 5.4.3).¶
The assertion carries no top-level user subject. The resulting ID-JAG carries the IdP-resolved subject and a fresh child handle, including for a terminal target. Issuing an onward ID-JAG preserves the target's grant redemption interface. Target-side resolution of a reference would require additional target processing; it and a direct recipient-bound credential remain open alternatives (Appendix C).¶
Asymmetric signing avoids distributing signing secrets between CAIs and IdPs. One compact JWS representation reduces implementation choices (Section 3.1). TLS protects transport; the profile does not conceal assertion contents from the carrying workload. Encryption and nested JWTs are outside this profile.¶
A handle identifies the hop being continued, so the IdP can derive the correct ancestry and apply revocation to the appropriate subtree. A chain-wide identifier alone would not distinguish sibling branches. The handle references IdP-held state without carrying user identity or the ancestry itself (Section 4, Section 5.5.5); its visibility still has correlation implications (Section 10).¶
Keeping authorization and lifecycle state at the IdP supports current checks at each continuation. The cost is state retained for the chain's lifetime and IdP availability for each exchange. Stateless hop commitments remain an open question (Appendix C).¶
In [RFC8693], act normally records a delegation relationship, the party
acting for the subject of the token that carries it. In an Identity
Continuation Assertion it instead identifies the authenticated actor
associated with the accepted authorization context, whose continuation request
the IdP evaluates (Section 3.2, Section 5.5.3); it asserts no
downstream authority.¶
The current actor's canonical identity in act records who acted,
independently of the credential used to authenticate it (Section 5.5.2).
The onward ID-JAG's client_id identifies that actor's OAuth client at the
target RAS. Keeping these identities distinct preserves lineage across
target-specific client registrations while retaining ID-JAG redemption
compatibility. The actor
therefore needs a client identity resolvable at the target
(Section 5.5).¶
The may_act claim ([RFC8693], Section 4.4) can inform actor authorization;
it supplies neither RAS acceptance evidence nor target subject resolution.¶
Each continuation is authorized under the recorded chain authorization and current policy (Section 5.1.4). RAS acceptance establishes the context from which the actor continues, and no more (Section 9.2): scopes at different audiences have independent semantics, so cross-target restrictions belong in the chain authorization and IdP policy rather than in a RAS's local scopes.¶
This profile carries identity and lineage and binds continuation to an accepted authorization. Whether a requested action serves the work the user or tenant authorized remains a deployment policy decision. The profile defines neither a purpose claim nor an agent authorization model.¶
Single-use limits each assertion to one issued grant. The lifetime bound limits how long the assertion can authorize a new continuation (Section 5.5.7).¶
This non-normative appendix illustrates three deployments: a tool gateway that selects its upstream at request time (Appendix B.1), a SaaS chain across three domains with separate CAIs (Appendix B.2), and an unattended background agent (Appendix B.3). The gateway example is the baseline. Authorization outcomes illustrate deployment policy rather than conformance requirements.¶
Message sequences are vertical lifelines with time flowing downward. Each example identifies its deployment topology before describing the flow.¶
Labels distinguish payloads and state: "On the wire" crosses a trust boundary, "Intra-domain context" stays within one trust domain, and "Server-side state" is never transmitted.¶
Continuation handles are numbered H0, H1, and so on, one per hop.¶
JWTs are shown as decoded payloads with JOSE headers and signatures omitted; client authentication is omitted except where shown; proof of possession uses DPoP.¶
The gateway example uses a root client without proof of possession; the SaaS example uses a root RAS that sender-constrains its access token by its own policy (Section 5.1). From the first continuation on, every continuation-aware RAS binds its token to the proven key (Section 5.2).¶
AgentApp calls a tool gateway on Alice's behalf. The gateway selects the upstream API, here a wiki, at request time. AgentApp knows only the gateway's audience. The gateway obtains an ID-JAG for the wiki without receiving Alice's identity assertion.¶
Topology: co-located. GatewayRAS accepts ID-JAGs and acts as CAI for those hops. It includes the handle in its access tokens.¶
All parties trust one enterprise IdP at https://idp.example/, tenant
tenant-123. Alice has pairwise subjects at GatewayRAS and WikiRAS, which
only the IdP can map.¶
Agent domain (agent.example): client agent-app, the confidential runtime
that holds Alice's session and roots the chain.¶
Gateway domain (gateway.example): GatewayRAS, the gateway's authorization
server at https://ras.gateway.example/, and the workload tool-gateway,
the Resource Server at https://gateway.example/ that AgentApp calls.¶
Wiki domain (wiki.example): WikiRAS at https://ras.wiki.example/, an
ordinary ID-JAG authorization server in front of WikiAPI at
https://api.wiki.example/. It is terminal in this chain.¶
H0 is the root hop, bound at GatewayRAS; H1 is the wiki hop, which nobody continues.¶
AgentApp IdP GatewayRAS/CAI ToolGateway WikiRAS/API
| | | | |
|-ID Token-->| | | |
|<-ID-JAG H0-| | | |
|-jwt-bearer: ID-JAG-------->| bind H0 | |
|<--access token, H0 claim---| | |
|----------tool call with AT----------------->| |
| | |<--exchange AT--| |
| | |-assertion H0-->| |
| |<--assertion + DPoP-------------| |
| |-----------ID-JAG H1----------->| |
| | | |-ID-JAG, DPoP->|
| | | | (no binding)|
| | | |<---wiki AT----|
| | | |-call WikiAPI->|
|<-------------------result-------------------| |
¶
The exchanges below presuppose the following configuration. Every item is an
ordinary OAuth or ID-JAG registration except the four the profile adds: the
IdP's continuation policy, its trust in GatewayRAS as an assertion issuer, its
advertisement of identity_continuation_supported, and GatewayRAS's
advertisement of the continuation profile.¶
IdP (Section 5.5.2, Section 9.4, Section 5.1, Section 7.1):¶
Register agent-app and tool-gateway as confidential clients in
tenant-123, with canonical actor identities (https://agent.example/,
agent-app) and (https://gateway.example/, tool-gateway).¶
Authorize https://gateway.example/ to issue tool-gateway's client
credential.¶
Trust GatewayRAS's issuer, https://ras.gateway.example/, and its signing
keys to attest its hops. Authorize GatewayRAS and https://gateway.example/
as a CAI and actor identity authority pair for tenant-123.¶
Configure tenant policy to grant agents read access to productivity tools
and permit tool-gateway to continue.¶
Advertise identity_continuation_supported and hold tool-gateway's
client identifier at WikiRAS, placed in the onward ID-JAG's client_id.¶
GatewayRAS (Section 7.2, Section 5.4.3):¶
Advertise the continuation grant profile and trust the IdP as ID-JAG issuer.¶
Register tool-gateway as an OAuth client associated with the resource it
operates, https://gateway.example/. The CAI uses this association when
checking whether it can issue an assertion for a token presented to that
resource.¶
ToolGateway (Section 5.5.2): provision a DPoP key pair and a
client-authentication credential issued by https://gateway.example/.¶
WikiRAS (base profile): register tool-gateway as a client, trust the IdP
as ID-JAG issuer, and support the DPoP-bound JWT grant.¶
ToolGateway authenticates to the IdP with the following [RFC7523] client assertion issued by the gateway domain (Appendix B.1.5). Its earlier exchange at GatewayRAS uses a separate client assertion addressed to GatewayRAS (Appendix B.1.4).¶
{
"iss": "https://gateway.example/",
"sub": "tool-gateway",
"aud": "https://idp.example/",
"iat": 1710000230,
"exp": 1710000290,
"jti": "tg-cred-01"
}
¶
In this example, iss and sub match ToolGateway's configured canonical actor
identity, used in the assertion's act and onward lineage
(Section 5.5.2, Section 3.2).¶
AgentApp exchanges Alice's ID Token, whose sid anchors the chain to her IdP
session (Section 5.1), for an ID-JAG addressed to GatewayRAS, the
one audience it knows:¶
POST /token HTTP/1.1 Host: idp.example Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:token-exchange &requested_token_type=urn:ietf:params:oauth:token-type:id-jag &audience=https://ras.gateway.example/ &resource=https://gateway.example/ &scope=tools.invoke &subject_token=<id_token> &subject_token_type=urn:ietf:params:oauth:token-type:id_token &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion=<agent-app JWT>¶
In this deployment, the chain authorization permits tool-gateway to
continue on Alice's behalf with read access to productivity services. The
IdP establishes a chain and embeds H0 (Section 5.1).¶
The authorization uses the tenant's current classification of productivity services. A wiki added to that class can therefore be reached without a new chain, while removing it prevents further continuation to it. That is deployment policy, not a requirement of this profile (Section 5.1.4, Section 9.2).¶
On the wire (decoded ID-JAG for GatewayRAS):¶
{
"iss": "https://idp.example/",
"aud": "https://ras.gateway.example/",
"sub": "gateway-pairwise-subject",
"client_id": "agent-app",
"resource": "https://gateway.example/",
"scope": "tools.invoke",
"auth_time": 1710000200,
"acr": "urn:example:loa:2",
"amr": ["pwd", "mfa"],
"identity_continuation_handle": "Qm7zXu2VtL9pKe4RaW1nHc",
"iat": 1710000205,
"exp": 1710000505,
"jti": "idjag-gateway-01"
}
¶
AgentApp redeems the ID-JAG at GatewayRAS with the jwt-bearer grant, exactly as for any ID-JAG, and nothing in this profile asks it to sender-constrain the exchange; GatewayRAS issues a bearer access token here, its own policy choice (Section 5.2):¶
POST /token HTTP/1.1 Host: ras.gateway.example Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer &assertion=<the ID-JAG above> &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion=<agent-app JWT>¶
GatewayRAS advertises the continuation grant profile (Section 7.2) and so
recognizes the handle. It binds H0, the issuing IdP, and the tenant it
associates with that IdP to the authorization state behind the access token,
in the same outcome that issues the token (Section 5.2); the ID-JAG
carried no cnf, so there is no key to record.¶
Server-side state at GatewayRAS:¶
{
"identity_continuation_handle": "Qm7zXu2VtL9pKe4RaW1nHc",
"status": "ACCEPTED",
"continuation_eligible": true,
"authorization_state": "gw-authz-7a1e",
"idp": "https://idp.example/",
"tenant": "tenant-123",
"client_id": "agent-app",
"bound_at": 1710000210
}
¶
GatewayRAS issues the access token as a signed JWT that carries H0 as a claim, the access-token carrier of Section 8.1.¶
On the wire (decoded access token):¶
{
"iss": "https://ras.gateway.example/",
"aud": "https://gateway.example/",
"sub": "gateway-pairwise-subject",
"client_id": "agent-app",
"scope": "tools.invoke",
"identity_continuation_handle": "Qm7zXu2VtL9pKe4RaW1nHc",
"iat": 1710000210,
"exp": 1710000810,
"jti": "at-gw-0001"
}
¶
AgentApp calls the gateway with this token. It chooses which token to present, and with it which authorization, but cannot alter the handle inside the token. A RAS that sender-constrains its tokens closes the bearer-ingress exposure of Section 9.1; Appendix B.2 shows that variant.¶
ToolGateway validates the access token as any resource server would.
Resolving the tool call, it selects the wiki as the upstream. To
continue Alice's chain there it needs an Identity Continuation Assertion,
which it obtains by exchanging the access token it just received at
GatewayRAS's token endpoint, authenticating as the OAuth client tool-gateway
and proving its own key (Section 5.4.1):¶
POST /token HTTP/1.1 Host: ras.gateway.example Content-Type: application/x-www-form-urlencoded DPoP: <proof signed by the tool-gateway key> grant_type=urn:ietf:params:oauth:grant-type:token-exchange &requested_token_type=urn:ietf:params:oauth:token-type:identity-continuation &subject_token=<the access token above> &subject_token_type=urn:ietf:params:oauth:token-type:access_token &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion=<tool-gateway JWT>¶
The DPoP proof on this request proves the tool-gateway key, which GatewayRAS
places in the assertion's cnf; had the access token been sender-constrained,
its own cnf would not be matched against this proof
(Section 9.1).¶
GatewayRAS confirms the token is its own, unexpired, and addressed to
https://gateway.example/, the resource tool-gateway is registered to
operate. It reads H0 from the token, rechecks that the authorization remains
active and that its binding still records it as eligible for continuation,
then issues the assertion bound to the proven key (Section 5.4.3).¶
On the wire (issuance response):¶
{
"issued_token_type": "urn:ietf:params:oauth:token-type:identity-continuation",
"access_token": "<the assertion below, compact JWS>",
"token_type": "N_A",
"identity_continuation_authorization_server": "https://idp.example/",
"expires_in": 120
}
¶
The identity_continuation_authorization_server identifies the IdP from which
ToolGateway obtains the next ID-JAG.¶
On the wire (decoded assertion):¶
{
"iss": "https://ras.gateway.example/",
"aud": "https://idp.example/",
"identity_continuation_handle": "Qm7zXu2VtL9pKe4RaW1nHc",
"act": {
"iss": "https://gateway.example/",
"sub": "tool-gateway"
},
"cnf": {
"jkt": "base64url-tool-gateway-key-thumbprint"
},
"iat": 1710000230,
"exp": 1710000350,
"jti": "n3Vt7Jq2Xd5PbR9wLc1eKs"
}
¶
ToolGateway resolves the token endpoint of https://idp.example/, the
identity_continuation_authorization_server it was given, using
Section 5.4.4, and presents the assertion there as the subject_token
of a continuation exchange, with client authentication and a DPoP proof of the
assertion's cnf key, requesting an ID-JAG for WikiRAS. The IdP maps the
registered client tool-gateway to its canonical actor identity
(Section 5.5.2):¶
POST /token HTTP/1.1 Host: idp.example Content-Type: application/x-www-form-urlencoded DPoP: <proof signed by the tool-gateway 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.wiki.example/ &resource=https://api.wiki.example/ &scope=wiki.read &subject_token=<identity-continuation-assertion> &subject_token_type=urn:ietf:params:oauth:token-type:identity-continuation &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion=<tool-gateway client assertion>¶
The IdP validates the exchange (Section 5.5.3):¶
Issuer trust: the assertion is signed by GatewayRAS, the RAS recorded for H0's hop, which that rule accepts directly.¶
Chain state: H0 names an accepted hop on an active chain.¶
Current actor and binding: the act claim names tool-gateway, the
authenticated client, and the DPoP proof matches cnf.¶
Authorization: the chain authorization permits tool-gateway to
obtain wiki.read for Alice, and current policy classifies the wiki as an
eligible productivity service. The root tools.invoke scope does not
independently authorize this access.¶
The IdP resolves Alice's wiki subject and issues the ID-JAG with H1 and
tool-gateway atop agent-app in the lineage.¶
On the wire (decoded ID-JAG for WikiRAS):¶
{
"iss": "https://idp.example/",
"aud": "https://ras.wiki.example/",
"sub": "wiki-pairwise-subject",
"client_id": "tool-gateway",
"resource": "https://api.wiki.example/",
"scope": "wiki.read",
"auth_time": 1710000200,
"acr": "urn:example:loa:2",
"amr": ["pwd", "mfa"],
"identity_continuation_handle": "Zp3kRt8VbN2wLq6YsD4mXe",
"act": {
"iss": "https://gateway.example/",
"sub": "tool-gateway",
"act": {
"iss": "https://agent.example/",
"sub": "agent-app"
}
},
"cnf": {
"jkt": "base64url-tool-gateway-key-thumbprint"
},
"iat": 1710000235,
"exp": 1710000535,
"jti": "idjag-wiki-01"
}
¶
A target not permitted by the chain authorization and current policy
fails with invalid_target; the chain stays continuable and only that tool
call fails (Section 5.5.6). Appendix B.3.4 illustrates a grant
that excludes a newly requested target.¶
ToolGateway redeems the ID-JAG at WikiRAS with the DPoP-bound JWT grant and a DPoP proof of the same key (Section 5.5.5). WikiRAS follows the base profile: it validates the ID-JAG, ignores the handle, and issues an access token without binding H1 (Section 5.2). ToolGateway calls WikiAPI as Alice's wiki subject and returns the result to AgentApp.¶
Further calls to WikiAPI for Alice reuse this access token while it remains valid and covers the requested access (Section 8). Access to another upstream repeats Appendix B.1.4 and creates a sibling hop under H0 (H2, and so on).¶
The gateway domain adds two things to an ordinary OAuth deployment:¶
GatewayRAS binds the handle when it redeems a continuation-capable ID-JAG, records whether the authorization is eligible for continuation, advertises the continuation grant profile alongside the base grants, places the handle in the access token, and issues assertions from its token endpoint (Section 5.2, Section 5.4, Section 7.2).¶
tool-gateway exchanges the access token it received for an assertion, then
presents that assertion once (Section 5.5.7), with its client
credential and a DPoP proof, to the IdP for the next ID-JAG.¶
Both presuppose registrations that ID-JAG already requires: tool-gateway is an
OAuth client of the IdP, holding a client-authentication credential and the key
its DPoP proofs use, and a client known to WikiRAS, which the onward ID-JAG
names as client_id (Section 5.5).¶
AgentApp and WikiRAS use the base profile unchanged. AgentApp proves no key anywhere and never shares Alice's identity assertion with the gateway. The IdP establishes the chain and evaluates each continuation (Section 5.1, Section 5.5).¶
A user's request crosses three SaaS domains: ExpenseApp calls ExpenseAPI,
whose workload calls TravelAPI, whose workload calls BookingAPI. Each domain
has its own Resource Authorization Server, and all trust one enterprise IdP
at https://idp.example/. Appendix B.2.6 lists what differs from
the gateway example.¶
Topology: separate CAI with a Transaction Token carrier.¶
Expense domain (expenses.example): client expense-app; ExpenseRAS,
Expense Transaction Token Service (TTS), and Expense CAI at ras., tts.,
and cai.expenses.example; and the workload expense-service behind
ExpenseAPI.¶
Travel domain (travel.example): TravelRAS, Travel TTS, and Travel CAI,
and the workload travel-service behind TravelAPI.¶
Booking domain (booking.example): BookingRAS in front of BookingAPI. It
runs the base ID-JAG profile and is terminal in this chain.¶
Each domain is the identity authority for its own workloads, so the IdP has
the canonical actor identities (https://expenses.example/, expense-app),
(https://expenses.example/, expense-service), and
(https://travel.example/, travel-service) recorded for these clients, the
values their act entries carry (Section 5.5.2).¶
The user has a pairwise subject at each RAS, which only the IdP can map. H0 is bound at ExpenseRAS, H1 at TravelRAS; H2 reaches BookingRAS, which never binds it.¶
Hop 0 roots the chain at ExpenseRAS and carries H0 to the workload:¶
ExpenseApp IdP ExpenseRAS Expense TTS ExpenseService
| | | | |
|-ID Token-->| | | |
|<-ID-JAG H0-| | | |
|-jwt-bearer: ID-JAG, DPoP->| bind H0 | |
|<-----------AT1------------| | |
|---------------call ExpenseAPI: AT1 + DPoP--------------->|
| | | |<-----AT1-----|
| | |<-resolve AT1--| |
| | |---bound H0--->| |
| | | |-TT with H0-->|
¶
Hop 1 continues to TravelRAS; hop 2 repeats it from Travel to Booking:¶
ExpenseService Expense CAI IdP TravelRAS Travel TTS/API
| | | | |
|-exchange TT-->| | | |
|<--assertion---| | | |
|--assertion, actor, DPoP-->| | |
|<--------ID-JAG H1---------| | |
|-------jwt-dpop: ID-JAG + DPoP--------->| bind H1 |
|<------------------AT2------------------| |
|-------------call TravelAPI: AT2 + DPoP-------------->|
| | | | TT with H1 to workload
¶
ExpenseApp exchanges the user's ID Token, whose sid anchors the chain to the
IdP session, for an ID-JAG addressed to ExpenseRAS:¶
POST /token HTTP/1.1 Host: idp.example Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:token-exchange &requested_token_type=urn:ietf:params:oauth:token-type:id-jag &audience=https://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 &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion=<expense-app JWT>¶
In this deployment, the chain authorization permits designated workloads to continue to Expense, Travel, and Booking. Current enterprise policy also permits that access (Section 5.1.4).¶
Server-side state (IdP authorization, illustrative):¶
(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 IdP creates the root hop H0 and embeds it in the ID-JAG (Section 4).¶
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",
"iat": 1710000005,
"exp": 1710000305,
"jti": "idjag-expense-01"
}
¶
ExpenseApp redeems the ID-JAG at ExpenseRAS for an access token, AT1, and ExpenseRAS binds H0, the issuing IdP, and the tenant it associates with that IdP to the authorization state behind AT1, and records that authorization as eligible for continuation (Section 5.2). The carrier is what differs: the handle stays in this record, and AT1 does not carry it.¶
Server-side state at ExpenseRAS:¶
{
"identity_continuation_handle": "kW4uJ8pTe2NxA6rQvD1zYs",
"status": "ACCEPTED",
"continuation_eligible": true,
"authorization_state": "at1-authz-2f9c",
"idp": "https://idp.example/",
"tenant": "tenant-123",
"client_id": "expense-app",
"bound_at": 1710000010
}
¶
ExpenseApp calls ExpenseAPI with AT1. The Expense TTS resolves AT1 against that
record over its own-domain interface with ExpenseRAS, derives H0, and issues a
Transaction Token for expense-service, the workload that completes the
request (Section 5.3). The token's req_wl names expense-api,
which requested the Transaction Token; expense-service is the workload
assigned to continue the transaction. The TTS records the assignment to that
workload in authenticated routing state, the domain's own record of which
workload handles a transaction. Neither ExpenseApp nor expense-service
supplies H0.¶
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 Transaction Token stays inside expenses.example. The
tctx.identity_continuation encoding is illustrative; the profile standardizes
no carrier schema.¶
expense-service presents the Transaction Token as the subject_token at
Expense CAI's token endpoint, authenticating as a client and proving its own
key (Section 5.4.1):¶
POST /token HTTP/1.1 Host: cai.expenses.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:identity-continuation &subject_token=<the Transaction Token above> &subject_token_type=urn:ietf:params:oauth:token-type:txn_token &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion=<expense-service JWT>¶
Expense CAI verifies the Transaction Token and reads H0. It checks the routing
state that assigns this transaction to expense-service; the domain audience
and req_wl alone do not establish that assignment. It rechecks with
ExpenseRAS that H0's authorization remains active and is eligible for
continuation, then issues the assertion bound to the proven key
(Section 5.4.3).¶
The IdP accepts its assertions for hops ExpenseRAS accepts because it trusts Expense CAI for that RAS from tenant configuration at the IdP; the rest of the conjunctive trust rule applies as it does to a co-located RAS (Section 7.3, Section 9.4).¶
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"
}
¶
expense-service presents the assertion to the IdP with an [RFC7523] client
assertion and a DPoP proof of the continuation assertion's cnf key, requesting
an ID-JAG for TravelRAS. The IdP maps the authenticated client to its canonical
actor identity (Section 5.5.2):¶
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 &client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer &client_assertion=<expense-service client assertion>¶
The IdP validates the exchange (Section 5.5.3):¶
Issuer trust: Expense CAI is trusted for ExpenseRAS, the RAS recorded for H0, rather than being that RAS itself.¶
Chain state: H0 names an accepted hop on an active chain.¶
Current actor and binding: act names expense-service, the authenticated
client, and the DPoP proof matches cnf.¶
Authorization: TravelRAS, TravelAPI, and trips.read are permitted by the
chain authorization and current policy.¶
The IdP never calls ExpenseRAS; the assertion is its evidence of acceptance
(Section 5.2.1). It resolves the user's Travel subject, creates H1 as a
child of H0, and places expense-service atop the root actor expense-app
in the lineage (Section 5.5.5).¶
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"
}
¶
expense-service redeems the ID-JAG at TravelRAS for AT2, and TravelRAS binds
H1 exactly as ExpenseRAS bound H0. The Travel TTS derives H1 into a Transaction
Token for travel-service. Within the illustrated
tctx.identity_continuation object, only the handle changes:¶
Intra-domain context (excerpt):¶
"tctx": {
"identity_continuation": {
"iss": "https://idp.example/",
"tenant": "tenant-123",
"handle": "Uc9fB3mHs5LdK7gEnX2wRj"
}
}
¶
travel-service obtains an assertion for H1 from Travel CAI and exchanges it
for an ID-JAG with audience=https://ras.booking.example/,
resource=https://api.booking.example/, and scope=stays.book, all permitted
by the chain authorization and current policy. It authenticates with its
own client assertion and proves the key in the continuation assertion's cnf.
The IdP resolves its canonical actor identity (Section 5.5.2), creates
H2 under H1, and extends the lineage:¶
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"
}
}
}
}
¶
BookingRAS is terminal: it redeems the ID-JAG under the base profile, ignores
H2, issues AT3, and binds nothing (Section 5.2). travel-service calls
BookingAPI with AT3.¶
The CAI is a separate service in each continuing domain, which the IdP trusts for that domain's RAS from tenant configuration (Section 7.3); a co-located RAS needs no such record, though the same conjunctive trust applies to both (Section 9.4).¶
The handle never enters an access token; a Transaction Token Service derives it from the RAS binding for each request, and the workload presents that token to obtain the assertion.¶
Two continuing domains produce a three-entry lineage: two continuation actors and the root actor.¶
ExpenseRAS chooses to sender-constrain the root access token, so ExpenseApp presents DPoP proofs at the RAS and on its API call; that is RAS policy under the base profile. The gateway example uses a bearer root access token.¶
Alice configures a daily calendar briefing that runs without her presence. The chain is anchored to a grant rather than a session (Appendix B.3.5). This example also shows rejection of a target excluded by the grant but permitted by tenant policy.¶
Topology: separate CAI with a Transaction Token carrier.¶
Platform domain (platform.example): workload briefing-agent; PlatformRAS,
Platform TTS, and Platform CAI in front of TaskAPI; and the Scheduler, an
internal component that holds only the task identifier and triggers each
run.¶
Calendar domain (calendar.example): CalendarRAS in front of CalendarAPI.
It is terminal in every run.¶
Mail domain (mail.example): MailRAS in front of MailAPI, reached only in
Appendix B.3.4; likewise terminal.¶
The platform domain is the identity authority for briefing-agent, so the
IdP has the canonical actor identity (https://platform.example/,
briefing-agent) recorded for that client, the value its act entry carries
(Section 5.5.2).¶
H0 is bound at PlatformRAS to the task authorization and outlives Alice's session; 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 presents a refresh token from a grant
that permits continuation as the root exchange's subject token, so the chain
is anchored to that grant rather than to her session (Section 5.1,
Section 6). In this deployment, the grant permits only the Platform and
Calendar access needed for the task, and current policy permits that access.
The root ID-JAG targets PlatformRAS.¶
Server-side state (IdP grant authorization, illustrative):¶
(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 exchange and RAS binding follow the pattern of Appendix B.2.1 and Appendix B.2.2. PlatformRAS binds H0 to a durable task authorization that it keys by its own task identifier; the record holds no bearer credential.¶
Server-side state (PlatformRAS task authorization):¶
task_id: task-123 owner: alice actor: briefing-agent identity_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 continuation_eligible: true¶
Server-side state (Scheduler):¶
task_id: task-123¶
The Scheduler never receives H0 or any user, chain, or bearer credential;
task-123 identifies a row in PlatformRAS's own state and means nothing
outside the platform.¶
Each run begins with no user present, so H0 comes from state the platform holds:¶
Scheduler BriefingAgent Platform TTS
| | |
|---trigger--->| | task-123
| |-task-123+proof->|
| | | verify proof + task; derive H0
| |<-fresh TT(H0)---|
¶
The task identifier is not a secret and does not authorize a run. The
Scheduler's trigger authenticates and carries only task-123; BriefingAgent
authenticates to the Platform TTS and proves possession of its key; and the
TTS, after confirming that task-123 is active and that BriefingAgent is its
designated actor, derives H0 into a fresh Transaction Token
(Section 5.3). Neither the Scheduler nor BriefingAgent selects H0.¶
Each run performs assertion issuance, continuation exchange, and ID-JAG redemption:¶
BriefingAgent Platform CAI IdP CalendarRAS
| | | |
|--exchange TT->| | |
|<-assertion----| | |
|---------------------------->| |
| assertion + DPoP | |
|<----------------------------| ID-JAG(child) |
|-------------------------------------------->|
| ID-JAG |
|<--------------------------------------------| access token
| | | no binding (terminal)
¶
BriefingAgent exchanges the Transaction Token at Platform CAI's token endpoint
and presents the assertion to the IdP the response's
identity_continuation_authorization_server names, with its client credential
and a DPoP proof. Platform CAI applies Section 5.4.3 to durable
task state rather than to a live user's request: it authenticates
briefing-agent, verifies its key and transaction, and rechecks that
PlatformRAS's H0 authorization remains active.¶
The assertion and onward ID-JAG use the formats shown in Appendix B.2.3 and
Appendix B.2.4, with Platform CAI as the assertion issuer.
briefing-agent is both the root actor and the current actor here, so the
onward act merges them into one entry (Section 5.5.5). CalendarRAS is
terminal and issues the access token without binding the child hop. Each run's
child is a sibling, not a descendant, of the previous run's child.¶
Suppose the platform later extends the briefing to include unread mail,
which requires https://api.mail.example/ behind https://ras.mail.example/.
Alice's grant authorizes only Platform and Calendar access, so the IdP rejects
a continuation from H0 to MailRAS. A general tenant policy allowing mail
access does not by itself expand that grant (Section 5.5.3).¶
HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"error": "invalid_target"
}
¶
A different chain authorization could permit read access to productivity
services selected by current policy, as in Appendix B.1.2. Under
that authorization, adding Mail to the eligible services could permit
mail.read without a new chain. A request for mail.send still fails with
invalid_scope if that authorization or current policy excludes it. These
outcomes follow the deployment's authorization semantics, not how the IdP
stores the authorization. A target-specific failure leaves the chain
continuable for other authorized targets.¶
The root subject token is a refresh token, so the chain anchors to a grant and survives logout; grant-anchor support is optional (Section 6.1).¶
H0 is bound to a durable task authorization that PlatformRAS keys by task identifier; the Scheduler holds only that identifier, which is not a secret and authorizes nothing.¶
Every run derives H0 afresh from active task state and obtains a new assertion. Stealing the task record exposes H0 but not the agent key, and continuation still requires a fresh assertion while the authorization is active.¶
Each run creates a sibling child of H0 rather than a descendant of the previous run's child.¶
This example uses a user-present setup event. More generally, chain establishment requires an authorized root exchange whose subject token resolves to an active user session or supported grant anchor (Section 5.1, Section 6.1). Administrative policy alone, without such an anchor, cannot establish a chain.¶
This non-normative appendix identifies questions for Working Group review.¶
[[ To be removed before publication as an RFC ]]¶
Acceptance attestation. Could a recipient-bound design, in which the IdP binds a continuation credential to an intended actor, actor class, trust domain, or key, or a target-resolved design replace the CAI assertion while still supplying evidence of RAS acceptance and of the actor's association with that context, which today only a party in the RAS's domain can provide (Appendix A.2)?¶
Sender constraint. Should mutual-TLS confirmation be defined jointly with ID-JAG? Should the assertion and ID-JAG permit different proven keys, and how should supported methods be advertised (Section 5.5.2)?¶
Client establishment control. Should root clients be able to require or suppress chain establishment (Section 5.1)? The authors' current position is that establishment remains a tenant policy decision, so that existing clients need no change to participate in a chain.¶
Stateless handles. Can self-verifying handles preserve ancestry and subtree revocation within the recommended size bound, and at what privacy and operational cost (Section 4, Section 5.5.5)?¶
Idempotent recovery. Should optional support be discoverable, and if so should an IdP advertise that it offers recovery, the retry window, or both (Section 5.5.7.1)?¶
Limits. The document recommends that an assertion's lifetime not exceed 300 seconds, recommends that IdPs accept lifetimes of up to 300 seconds and leaves the maximum to deployment, and requires a finite hop-count limit without fixing a default (Section 3.2, Section 6.3). Should the 300-second recommendations become requirements, should the profile fix a hop-count default, and should the accepted maximum be advertised in metadata?¶
The project issue tracker also records WG questions on authorization bounds (#106), acceptance freshness (#107), actor identity evidence (#108), CAI discovery (#109), bearer ingress (#110), and acceptance accountability (#41): https://github.com/mcguinness/draft-mcguinness-oauth-id-continuation-assertion/issues¶
Further extension topics include client-requested limits or permitted actors, intra-domain actor lineage and audit ([I-D.mcguinness-oauth-actor-receipts], [I-D.mcguinness-oauth-actor-proofs]), non-user roots, and RAS-derived narrowing (Section 5.2.1).¶
The authors thank the Working Group participants who developed the OAuth Identity and Authorization Chaining Across Domains and the Identity Assertion JWT Authorization Grant specifications, on whose work this profile builds.¶
[[ To be removed before publication as an RFC ]]¶
-02¶
Replaced the fixed root envelope of -01 with chain authorization based on recorded root facts and current tenant policy. Clarified that the CAI attests acceptance, eligibility, and actor association; the IdP authorizes onward access.¶
Removed actor_token processing; client authentication determines the
canonical actor identity. Distinguished disclosed actor lineage from the
IdP's hop lineage.¶
Defined assertion issuance using Token Exchange at the CAI, with an access
token or Transaction Token and DPoP. Added the
identity_continuation_authorization_server response parameter and client
validation against the IdP's metadata issuer.¶
Simplified hop state to IdP issuance and RAS acceptance, with continuation eligibility attested by the CAI using live or self-contained evidence.¶
Removed the root exchange's proof-of-possession requirement. Specified DPoP-bound redemption of onward ID-JAGs and access-token binding at continuation-aware RASes.¶
Clarified chain lifetime, withdrawal, and limits; made grant-anchor support optional and specified base-profile behavior when no anchor can be resolved.¶
Defined optional recovery of an issued grant after a lost response while preserving single-use assertions. Clarified error codes and their precedence.¶
Relaxed assertion lifetime, jti entropy, and handle-length requirements;
permitted nbf; recommended capping assertion expiry at self-contained
evidence expiry; and registered the handle as an introspection response
member.¶
Removed RAS nomination of CAIs through identity_continuation_issuers,
clarified IdP configuration of CAI trust, and lifted the -01 restriction of
CAI issuance to the RAS's trust domain, which is now typical rather than
required.¶
Reorganized the protocol description and security considerations; revised the introduction, examples, design rationale, and open items.¶
Added Aaron Parecki as an author.¶
-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¶