| Internet-Draft | SPT-Txn Tokens | July 2026 |
| Coetzee | Expires 20 January 2027 | [Page] |
Current authorization is role-scoped: an actor is granted a role whose authority persists across every action it takes. This fails exactly when actors fail -- under compromise, prompt injection, or goal hijacking -- because a compromised actor retains full role authority. This document specifies SPT-Txn, a family of transaction-bound authorization tokens in which authority exists only inside a short-lived token bound to one declared action, on one resource, under one jurisdictional policy, verified against how the requesting workload was attested. Delegation across agents and tools is expressed as a cryptographically sealed chain that can only narrow authority and is verifiable offline. Each authorization decision emits a signed, tamper-evident receipt as a byproduct of enforcement. This document specifies normative intent binding, transaction receipts and their transparency log, attested issuance via OAuth 2.0 Token Exchange, per-token status-list revocation, and cryptographic algorithm agility including hybrid post-quantum signing. It also hardens attested issuance in response to an adversarial security review of the reference implementation: it makes an audience and a verifiable expiry mandatory on the presented attestation, requires the issuer to bound the delegated depth it grants, and clamps a token's lifetime to the attestation it was minted on. The issuer-side requested-versus-permitted scope intersection is retained; the enforcement point re-checks the chain independently.¶
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 20 January 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.¶
An autonomous software agent that holds a role-scoped credential holds, at every moment, the full authority of that role. When the agent is manipulated -- by a poisoned document it reads, an injected instruction in tool output, or a compromised planning loop -- that full authority is available to the attacker. Role scoping fails precisely at the moment agents fail.¶
SPT-Txn inverts the model. Authority is not attached to an actor for a session; it is minted per transaction, bound to a single declared action, and expires in seconds. A compromised agent holds a token that is cryptographically useless for any action other than the one it declared. This document specifies the token family, its delegation and attenuation semantics, the binding of a token to a declared intent, the evidence a decision emits, the issuance of tokens from attested workload identity, per-token revocation, and algorithm agility.¶
This revision is a correction-and-consolidation release. The published -02 contained two substantive errors, both fixed here, and it is refocused on the protocol wire format and its verification.¶
Terminology. The root token is named the Compliance Attestation Token (CAT) throughout. Prior revisions called it a "Capability Acquisition Token" and treated CAT and the Capability Token (CT) as synonyms; they are distinct roles -- a CAT is the root, and a CT is a delegated attenuation of it.¶
RFC 9700. Prior revisions cited RFC 9700 as "Transaction Tokens" and derived base claims from it. RFC 9700 is the OAuth 2.0 Security Best Current Practice; the Transaction Tokens work is the separate draft-ietf-oauth-transaction-tokens. This revision removes that conflation: the token model is specified self-contained (Section 3), not as an extension of RFC 9700 claims.¶
Self-contained token model. The core claims and the base verification steps are now specified in this document (Section 3, Section 3.3), with no dependency on an external base document.¶
Scope. Implementation- and deployment-specific material from -02 -- the zero-knowledge circuit constructions, the multi-chain trust-registry deployment notes, and the human-anchor escrow / threshold-custody mechanism -- is removed from the protocol specification. Those are deployment concerns, not required to specify the token format or its verification; the identity root and any lawful-access arrangement are explicitly out of scope (Section 3.2).¶
New normative sections. Intent binding (Section 5), transaction receipts and their transparency log (Section 6), attested issuance via OAuth 2.0 Token Exchange (Section 7), and status-list revocation (Section 8), reflecting a public reference implementation. Attested issuance is hardened following an adversarial review: a mandatory audience and a verifiable expiry on the presented attestation, an issuer-side bound on delegated depth, and a token lifetime clamped to its attestation.¶
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.¶
Actor: a human, workload, or AI agent that requests or holds authority.¶
Compliance Attestation Token (CAT): the root authorization token for an actor, establishing its maximum capability scope and delegation depth.¶
Capability Token (CT): a delegated token, a strict attenuation of its parent (a CAT or another CT).¶
Transaction Token (TXN): a short-lived, sender-constrained token bound to a single concrete transaction.¶
Policy Enforcement Point (PEP): the component that verifies a presented token against the actual request and permits or denies.¶
Intent: the declared action a token authorizes -- a tool/method identifier, a canonicalized parameter digest, and a target resource.¶
An SPT-Txn deployment issues three token types forming a chain of decreasing authority: CAT (root) -> CT (zero or more delegation hops) -> TXN (leaf, per-transaction). All are signed JSON Web Tokens [RFC7519]. Verification is offline: a PEP holding the presented tokens and a locally-cached trust-registry snapshot establishes authority without contacting the issuer.¶
Every token carries:¶
iss (string): the issuer identifier, resolvable in the trust registry to a
public key, a role, and a signing algorithm.¶
sub (string): the subject the token authorizes.¶
iat, exp (NumericDate): issued-at and expiry.¶
jti (string): a unique token identifier.¶
txn_token_type (string): one of CAT, CT, or TXN.¶
human_anchor (string): a privacy-preserving commitment to the authorizing
human (see Section 3.2).¶
holder_key (string): the holder's public key; the presenter MUST prove
possession of the corresponding private key when presenting the token (for
example via DPoP [RFC9449] or HTTP Message Signatures [RFC9421]).¶
A CAT additionally carries capability_scope (object), the maximum
authority, and delegation_depth_max (integer, zero or greater). It MAY carry
spt_attestation (the sealed attested-issuance evidence, Section 7)
and status (a status-list reference, Section 8).¶
A CT carries a capability_scope contained in its parent's,
delegation_depth_remaining (integer) decremented by exactly one from its parent
at each hop (Section 4), a binding to the exact bytes of its parent (a hash),
and MAY carry status.¶
A TXN additionally carries aud (the intended relying party), a
transaction-context hash binding it to one concrete transaction, and the intent
digest (Section 5). Its exp SHOULD be short, on the order of seconds.¶
The human_anchor claim is a commitment to the identity of the human who
authorized the root CAT. It carries no name and no PII. It is propagated
unchanged from the CAT into every downstream CT and TXN; a PEP MUST reject a
chain in which human_anchor is not byte-for-byte identical at every hop. An
issuer MAY freshly randomize the commitment per CAT issuance so anchors are
unlinkable across issuances.¶
The establishment of the anchor -- proving it belongs to one genuine, unique human -- is out of scope for this document. It is supplied by an identity root behind an implementation-defined interface (for example a proof-of-personhood credential); this document specifies only the claim, its propagation, and its consistency requirement. No part of the token chain is dependent on any single identity-root provider.¶
A PEP verifies a presented chain offline, against a locally-cached trust-registry snapshot, in the following steps. Each MUST fail closed:¶
Signature. Verify each token's signature under the issuer's registered key, using the algorithm bound in the trust registry, not the token header (downgrade resistance).¶
Temporal validity. Reject any token outside its iat/exp bounds, within
bounded clock skew.¶
Audience. For a TXN, verify aud matches this relying party.¶
Revocation. If a token carries status, check it against a cached
status list (Section 8); an unavailable status snapshot fails closed.¶
Sender constraint. Verify the presenter possesses the key in holder_key
(for example a valid DPoP proof [RFC9449]).¶
Chain. Verify the full chain from the root CAT; each hop MUST commit to the exact bytes of its parent, so no validly-signed token can be spliced under a parent it was not delegated from (Section 4).¶
Scope. Evaluate the request against the chain-intersection of every
capability_scope from root to leaf (Section 4).¶
Context binding. Verify the TXN's transaction-context hash matches the actual transaction and its bound intent digest (Section 5).¶
These claims and steps are the base model; the sections that follow extend it with delegation attenuation, intent binding, transaction receipts, attested issuance, status-list revocation, and algorithm agility.¶
A delegation chain is a sequence CAT -> CT_1 -> ... -> CT_n. Each hop MUST satisfy the following invariants, all enforceable offline:¶
Monotonic scope. For every hop, scope(child) is contained in scope(parent). Scope dimensions are constraints: numeric dimensions are ceilings, string and boolean dimensions require equality, list dimensions require the subset relation, and object dimensions are contained recursively.¶
Effective scope is the chain intersection. A dimension present in a parent but absent in a child is a constraint that the child did not restate, not a relaxation. A PEP MUST evaluate a transaction against the intersection of every scope from the root to the leaf, inheriting a dropped dimension from its nearest ancestor that declares it. A hop that omits a ceiling therefore cannot escape that ceiling.¶
Monotonic TTL. exp(child) MUST NOT exceed exp(parent). An implementation MAY clamp a defaulted lifetime to the parent boundary but MUST reject an explicitly requested lifetime that would exceed it.¶
Bounded depth. The root declares a maximum delegation depth. Each hop decrements the remaining depth by exactly one. A hop at depth zero MUST be rejected. Absent, non-integer, or negative depth MUST be rejected.¶
Full-chain verification. A PEP MUST verify the entire chain from the root. There is no fast path for a "trusted" intermediate token. Each hop commits to the exact bytes of its immediate parent (a hash), so no validly-signed token can be spliced under a parent it was not delegated from.¶
The caveat language MUST be provably narrowing: if it can express "allow X" as well as "deny unless X", it is unsafe and MUST be restricted. Implementations SHOULD verify monotonicity with property-based tests over randomly generated chains asserting that authority never widens at any hop.¶
A Transaction Token MAY carry an intent digest that binds the token to a single declared action. A PEP that enforces intent binding recomputes the digest over the actual call and compares.¶
The declared intent is the JSON object:¶
{ "tool": <string>, "params": <object>, "target": <string> }
¶
where tool is the tool or method identifier, params is the declared
parameter object, and target is the resource or service identity the action
executes against.¶
The intent digest is computed over the JSON Canonicalization Scheme [RFC8785] serialization of the intent object, restricted to the accepted subset in this section. Anything outside the subset MUST be rejected, never normalized.¶
Object member names MUST be unique. A duplicate member MUST be rejected at parse time (not resolved last-wins).¶
Numbers MUST be integers with absolute value at most 2^53-1, expressed without fraction or exponent and without negative zero. Monetary amounts and other precise quantities MUST be represented as strings.¶
Strings MUST be valid UTF-8 and MUST NOT contain U+FFFD.¶
Object members are ordered by member name as sequences of UTF-16 code units, per [RFC8785].¶
Nesting depth is bounded; exceeding the bound MUST fail closed.¶
A single canonicalization implementation MUST be shared by the issuer path (that computes the bound digest) and the verifier path (that recomputes it). Two implementations will diverge over time, and a divergence is a full authorization bypass. Implementations MUST differentially test the canonicalizer against an independent implementation and MUST fuzz it.¶
intent_digest = base64url( SHA-256( "spt-txn-intent-v1" || 0x00 || JCS(intent) ) )¶
carried as the spt_intent_digest claim (base64url, unpadded). The PEP
recomputes the digest over the actual call and compares in constant time.
A mismatch MUST result in denial. A token presented to an intent-enforcing PEP
without an spt_intent_digest claim MUST be denied; absence never downgrades
enforcement.¶
Intent binding is the direct mitigation for goal hijacking (OWASP ASI01 [OWASP-ASI]): an agent whose reasoning is manipulated mid-task holds a token that is cryptographically useless for the hijacked action. It does not, and does not claim to, evaluate whether the declared intent is itself wise; that is the policy layer's responsibility.¶
An issuer or PEP that emits evidence MUST emit a signed Transaction Receipt at the moment of each decision, including denials.¶
A receipt is a JSON object with: a version string; the PEP identity; a decision of "PERMIT" or "DENY"; a class of "ok", "violation" (a check failed), or "unavailable" (a dependency was unreachable); the rule path that fired; the base64url SHA-256 hash of the presented token; the hash of the policy bundle version evaluated; the bound intent digest if any; the jurisdiction profile; a timestamp; and a nonce. The receipt is signed with the log signing key, which MUST be separate from the token issuance key and rotate on a separate schedule.¶
The signing input is "spt-txn-receipt-v1" || 0x00 || JCS(receipt-without-sig),
using the same canonicalization as Section 5.2. Receipts MUST NOT carry
payloads or personally identifiable information -- hashes and references only.¶
Operators MUST be able to distinguish a "violation" (an attack) from an "unavailable" (an outage); the two decision classes are therefore mandatory and distinct.¶
Receipts are appended to an append-only log whose periodic Merkle tree heads [RFC6962] are signed and co-signed by at least one external witness. Any single receipt is inclusion-provable against a signed tree head without revealing the rest of the log. A compromised operator cannot produce a consistent alternate history that a witness will co-sign.¶
If the log is unreachable, a deployment MUST decide explicitly, and document, whether decisions proceed with a durable append-only local buffer reconciled on reconnect, or whether log-unavailability is a fail-closed condition. This MUST NOT default silently.¶
An issuer SHOULD issue tokens only from an attested identity, never a bearer secret alone. Attested workload identity -- SPIFFE JWT-SVID or X.509-SVID [SPIFFE], a Kubernetes projected ServiceAccount token, or a cloud workload-identity assertion (for example AWS IRSA, GCP Workload Identity Federation, or an Azure federated credential) -- is presented as the subject token of an OAuth 2.0 Token Exchange [RFC8693]. The issuer:¶
Verifies the attestation against the trust domain's keys (never the
workload's self-report). The signature algorithm MUST be constrained to an
allowlist; alg: none and unexpected algorithms MUST be rejected before any
verification key is selected. For SPIFFE SVID and cloud/federation methods an
audience MUST be present and MUST bind the assertion to this exchange
endpoint; a missing audience on these methods MUST be rejected and MUST NOT
be treated as "any audience", so an assertion minted for another relying
party cannot be replayed here. A subject token that carries no verifiable
expiry (exp) MUST be rejected: a proof that never ages out is not a proof.¶
Computes an evidence digest
base64url(SHA-256("spt-txn-attest-v1" || 0x00 || evidence)) over the exact
presented evidence, and seals it, with the attested subject and method, into
the issued token as the spt_attestation claim. The raw subject token MUST NOT be logged or forwarded downstream; only its evidence digest is retained.¶
Grants scope as the intersection of the requested scope and the policy-permitted scope for the attested principal, and bounds the delegated depth it grants. An attestation establishes identity, not entitlement: a requested scope is a ceiling, never an instruction, and MUST NOT widen authority beyond what policy permits; an unbounded or arbitrarily large requested delegation depth MUST NOT be honored. The enforcement point independently re-evaluates the full chain intersection at execution (Section 4). Issuer-side intersection at mint and PEP-side enforcement at execution are defense in depth, not alternatives.¶
The token's expiry MUST NOT exceed the attestation's own expiry; a token cannot outlive the proof it was minted on. An implementation MAY clamp a defaulted token lifetime down to the attestation boundary but MUST reject a request whose token would outlive its proof. An issuer MAY enforce attestation-freshness predicates (e.g. "actions of class X require attestation newer than D seconds"), denying a stale proof with a decision class that distinguishes a freshness- predicate violation from an unavailable dependency (Section 6).¶
A CAT, CT, or TXN MAY carry a status claim referencing an entry in a Token
Status List [I-D.ietf-oauth-status-list]:¶
"status": { "status_list": { "idx": <uint>, "uri": <string> } }
¶
A PEP configured for status checking MUST, for every token in the chain that
carries a status claim, resolve the signed Status List Token for the URI from
its local cache and read the status at the index. A status of INVALID
(revoked) or SUSPENDED MUST result in denial. An unresolvable, expired, or
unsigned list, or an out-of-range index, MUST fail closed (deny). The signed
list is consumed offline; a PEP MUST NOT fetch status in the hot authorization
path. Status-list revocation complements, and does not replace, immediate
key-cascade revocation of a delegating issuer.¶
The signature suite identifier MUST be covered by the signature. The signing
input is "spt-txn-env-v1" || 0x00 || suite_id || 0x00 || payload, so forcing
a weaker suite requires forging the very signature it is trying to weaken.
Unknown suites MUST be rejected by allowlist.¶
A hybrid suite carrying both a classical (Ed25519) and a post-quantum (ML-DSA) signature MUST carry both signatures; an envelope missing one MUST be rejected as malformed in every mode, so a downgrade cannot be effected by omission. The verification mode -- accept-either (transition) or require-both (strict) -- is verifier configuration and MUST NOT be inferred from the token. A jurisdiction profile MAY pin a minimum suite, checked before signature dispatch, so a valid classical signature cannot pass a profile that requires hybrid.¶
Because transaction tokens are short-lived, algorithm migration is unusually cheap: a token outlives its algorithm by minutes, not years.¶
The dominant risk classes, in likelihood order, are: (1) canonicalization mismatch between the issuer and verifier intent-digest paths, which is a full authorization bypass and is addressed by Section 5.2; (2) attenuation bypass via a widening caveat or isolated child validation, addressed by Section 4, in particular the chain-intersection rule; (3) algorithm downgrade, addressed by Section 9; (4) replay, addressed by nonce binding and single-use enforcement at the PEP with a fail-closed replay cache; (5) parser vulnerabilities, addressed by strict parsing and continuous fuzzing; (6) revocation gaps, addressed by Section 8; (7) confused deputy at the PEP, which MUST act strictly on the token's authority and MUST NOT forward upstream credentials that outlive a single decision; and (8) authority inflation at attested issuance -- an attestation proves identity, not entitlement, so an issuer that treated a requested scope as a grant, or honored an unbounded delegation depth, would let any attested low-privilege workload mint arbitrary authority. This is addressed by Section 7: the issuer intersects the requested scope with the policy-permitted scope (requested scope is a ceiling), delegated depth is bounded at issuance, and the PEP independently re-checks the chain intersection at execution. A cross-service replay of an attestation minted for another relying party is addressed by the mandatory-audience rule in the same section.¶
Every error path in the trust boundary MUST deny, with a decision class distinguishing violation from unavailability. Deny-by-default SHOULD be a structural property of the implementation, such that a request that has not received a decision cannot be constructed, rather than a runtime check a refactor can silently remove.¶
The system constrains an agent to its declared intent; it does not defend against an agent that is authorized to perform a harmful action and does exactly that, nor against a compromised issuer with signing-key access (mitigated organizationally and detectably via the transparency log). Implementations MUST NOT overclaim these boundaries.¶
This document requests registration of the following JWT claims in the JSON Web
Token Claims registry: txn_token_type, human_anchor, holder_key,
capability_scope, delegation_depth_max, delegation_depth_remaining,
spt_intent_digest, and spt_attestation, together with the use of the
status claim per [I-D.ietf-oauth-status-list]. Full registration templates
will be provided in a subsequent revision.¶
The transparency-log design follows the Certificate Transparency [RFC6962] lineage. The attested-issuance profile builds on OAuth 2.0 Token Exchange [RFC8693] and SPIFFE [SPIFFE].¶