| Internet-Draft | TLS-Session-Bound-Tokens | June 2026 |
| Krishnan, et al. | Expires 22 December 2026 | [Page] |
This document defines a mechanism for binding OAuth 2.0 access tokens to a specific mutual TLS (mTLS) connection. The binding is achieved through a proof token that incorporates the TLS Exporter value (RFC 5705) derived from the current connection and an access token hash, signed by the client's private key corresponding to its mTLS certificate. This mechanism prevents stolen bearer tokens from being replayed on a different TLS connection. The proof is constructed once per (token, connection) pair and reused across all requests on that connection, delivering session binding with no per-request signing overhead and no additional key management beyond what mTLS already provides. The mechanism is applicable to TLS 1.2, TLS 1.3, and QUIC transports. While applicable to any OAuth 2.0 access token presented over mTLS, this specification is primarily motivated by the OAuth 2.0 Token Exchange protocol (RFC 8693), where multi-hop delegation chains in autonomous, agent-driven architectures create elevated replay risk.¶
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 22 December 2026.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
OAuth 2.0 access tokens are typically bearer tokens: any party in possession of the token can use it to access protected resources, regardless of the presenter's identity or the communication channel. This is a known risk addressed by the OAuth 2.0 Security Best Current Practice [I-D.ietf-oauth-security-topics].¶
The Token Exchange protocol [RFC8693] amplifies this risk by enabling chained delegation across service boundaries. Each exchange produces a new bearer token, and a compromise at any point in the chain exposes downstream tokens.¶
Existing mitigations address parts of this problem:¶
None of these mechanisms provide TLS-connection-level binding for OAuth 2.0 access tokens in mTLS environments. This specification fills that gap by reusing the existing mTLS key pair (no additional key generation) and amortizing the proof to once per (token, connection) pair rather than once per request — delivering stronger binding than DPoP at lower per-request cost. The binding mechanism relies on TLS Exporter values, which are available in TLS 1.2, TLS 1.3, and QUIC (via its integrated TLS 1.3 handshake), making the specification transport-agnostic across modern encrypted transports.¶
The rise of autonomous AI agents dramatically amplifies the bearer token replay risk. Unlike traditional OAuth flows where a human initiates discrete requests, agentic AI systems:¶
These characteristics make bearer token replay a first-order threat in agentic AI architectures. As autonomous agents increasingly drive API-to-API traffic volumes that exceed human-initiated requests, per-request cryptographic overhead becomes a significant scaling concern. This document addresses this gap by binding access tokens to the mTLS connection on which they are presented, with proof amortization that scales to high-volume agent traffic.¶
This specification applies to deployments where the entity verifying the Session-Binding Proof is a direct endpoint of the client's TLS connection — either the resource server itself or a sidecar co-located at the same trust boundary as the resource server. In both cases the verifier derives the TLS Exporter value (EKM) from a TLS session it directly participates in, and no EKM forwarding across an HTTP boundary is required.¶
Deployments where TLS is terminated at a remote intermediary that is not co-located with the resource server — for example, a standalone load balancer, CDN edge, or API gateway that conveys the request to the backend over a separate HTTP connection — are explicitly out of scope. Forwarding the EKM through such an intermediary would change the security property from "bound to the TLS session" to "bound to what a proxy claims the TLS session's EKM was," which is a categorically weaker guarantee. The reasons for this scope boundary, and the deployment patterns that satisfy it, are detailed in Section 5.¶
The primary target population is server-to-server, service-mesh, and agentic AI workloads where both endpoints have direct access to the TLS stack. Browser-based web clients are not in scope today, as TLS Exporter values are not currently exposed to JavaScript.¶
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:¶
This specification defines a proof-of-possession mechanism that binds OAuth 2.0 access tokens to the mTLS connection on which they are presented. While applicable to any OAuth 2.0 access token, it is primarily designed for tokens issued via the Token Exchange protocol [RFC8693], where multi-hop delegation creates elevated replay risk. The mechanism operates as follows:¶
A token that requires session binding includes a confirmation method claim (tls_exp) containing the TLS Exporter label, which signals to the resource server that the Session-Binding Proof MUST be presented and verified.¶
Both the client and resource server MUST derive the TLS Exporter value using the following parameters:¶
For TLS 1.3, the exporter is derived as specified in Section 7.5 of [RFC8446]. For TLS 1.2, the exporter is derived as specified in [RFC5705].¶
Note: By binding to the TLS Exporter rather than the application traffic keys, the binding remains valid across TLS 1.3 KeyUpdate operations. Standard key rotation refreshes traffic keys but does not change the exporter master secret, avoiding unnecessary re-proof cycles while maintaining strong connection binding.¶
Note: This mechanism requires both the client and resource server to access the TLS Exporter derivation function from their TLS library. Most TLS libraries (OpenSSL, BoringSSL, Go crypto/tls) provide this API (e.g., SSL_export_keying_material()). However, some application frameworks and proxy platforms may not yet surface this function to their HTTP or extension layer. Implementors should verify that their TLS integration exposes exporter derivation to the layer responsible for constructing or verifying the Session-Binding Proof.¶
The Session-Binding Proof is a JWT with the following structure:¶
{
"typ": "tls-binding-proof+jwt",
"alg": "ES256",
"x5t#S256": "<base64url SHA-256 thumbprint of client certificate>"
}
¶
The alg value MUST match the key type of the client's mTLS certificate. The x5t#S256 value MUST be the base64url-encoded SHA-256 thumbprint of the DER-encoded client certificate, as defined in [RFC8705].¶
The payload contains REQUIRED connection-level claims that prove session binding, and OPTIONAL per-request claims for intra-session hardening.¶
{
"ath": "<base64url SHA-256 hash of the access token>",
"ekm": "<base64url TLS Exporter value>",
"iat": 1710820000,
"jti": "<unique identifier>",
"htm": "POST",
"htu": "/api/resource"
}
¶
Note: The example above shows all defined claims. The ath, ekm, and iat claims are REQUIRED; the jti, htm, and htu claims are OPTIONAL per-request claims included only when intra-session hardening is required.¶
The payload claims are defined as follows:¶
iat window and the per-request claims.¶
The following claims provide intra-session request-level binding. They are OPTIONAL and intended for deployments that require defense-in-depth against intra-connection replay. When omitted, the proof can be reused across all requests on the same connection for the same token.¶
An authorization server that supports TLS-session-bound access tokens MUST include a cnf (confirmation) claim in the issued access token (when the token is a JWT) or in the token introspection response. The cnf claim MUST contain:¶
{
"cnf": {
"x5t#S256": "<cert-thumbprint>",
"tls_exp": "EXPORTER-oauth-tls-session-bound"
}
}
¶
cnf members which carry key/binding material rather than boolean flags (see [RFC7800]).¶
The mechanism for requesting and issuing TLS-session-bound tokens is as follows:¶
cnf claim with tls_exp set to the exporter label in the issued token.¶
tls_exp requirement.¶
The following diagram illustrates the complete flow:¶
Client (with cert C) Resource Server
| |
|=== mTLS handshake (client cert C) =================>|
| |
| [ONCE PER CONNECTION] |
| Both sides derive: |
| EKM = TLS-Exporter( |
| "EXPORTER-oauth-tls-session-bound", |
| "", 32) |
| |
| [ONCE PER (TOKEN, CONNECTION)] |
| Client constructs proof JWT: |
| header = { typ, alg, x5t#S256 } |
| payload = { |
| ath: SHA256(access_token), |
| ekm: EKM, |
| iat: <unix_timestamp> |
| } |
| sig = Sign(C.privateKey, header || payload) |
| |
|--- HTTP Request 1 -------------------------------->|
| Authorization: Bearer <access_token> |
| Session-Binding-Proof: <proof_jwt> |
| |
| Server verifies: |
| 1. sig matches C.publicKey from mTLS |
| 2. ath matches SHA256(access_token) |
| 3. ekm matches server-derived EKM |
| 4. iat within acceptable window |
| (caches: this token is bound to this conn) |
| |
|<-- 200 OK ----------------------------------------|
| |
|--- HTTP Request 2 (same token, same proof) ------->|
| Authorization: Bearer <access_token> |
| Session-Binding-Proof: <proof_jwt> (reused) |
| |
| Server verifies (per request): |
| - looks up (conn_id, ath) in cache |
| - cache hit: binding verified for THIS conn |
| |
|<-- 200 OK ----------------------------------------|
¶
Because the Session-Binding Proof contains only connection-level claims (ekm, ath, iat), the client constructs and signs the proof once per (token, connection) pair and reuses it for all subsequent requests on that connection. The iat value reflects the time of proof construction, not the time of any particular request. This provides the following profile relative to DPoP:¶
(connection, ath) binding and reduce subsequent verifications to a cache lookup; when this cache is used, server-side cost is also amortized to once per (token, connection). When the cache is not used (for example, in resource-constrained deployments that cannot afford per-connection state, or after cache eviction under memory pressure), server-side cost falls back to per-request JWT verification, which is comparable to DPoP. The amortization property is therefore unconditional on the client side and contingent on caching on the server side.¶
The server-side cache state, when used, is proportional to the number of distinct (connection, ath) pairs currently active. Each entry is small (a connection identifier plus a 32-byte token hash). Resource servers SHOULD bound cache size and SHOULD purge entries associated with a connection when that connection terminates; under memory pressure, eviction is safe at the cost of falling back to full verification on the next request for an evicted entry.¶
When OPTIONAL per-request claims (jti, htm, htu) are included, the proof must be constructed per request (similar to DPoP) and server-side caching of full proofs is not applicable. Deployments choose between per-connection efficiency and per-request intra-session hardening based on their threat model.¶
This amortization benefit assumes that mTLS connections are long-lived relative to the number of requests. If a deployment creates a new mTLS connection for every HTTP request-response cycle, the proof must be constructed on every request and the per-request cost becomes equivalent to DPoP. Deployments SHOULD use persistent connections (e.g., HTTP/2 or HTTP/1.1 with keep-alive) so that a single mTLS connection carries many requests. This is already standard practice in service mesh and workload-to-workload environments where mTLS is terminated at a sidecar or gateway.¶
A client MAY bind multiple tokens to a single mTLS connection concurrently. Each token requires its own Session-Binding Proof with a distinct ath (token hash) and the same ekm (TLS Exporter value) from the current connection.¶
This is common in workload-to-workload scenarios: a single mTLS connection between two microservices may carry requests on behalf of many different users, each with a distinct access token obtained via separate token exchanges. For example, 100 users interacting with an AI agentic service that delegates to a downstream service — each user's token is bound to the same mTLS connection via a separate proof. Similarly, when a token is refreshed or rotated mid-session, the client constructs a fresh proof for the new token without requiring a new mTLS connection.¶
The resource server MAY cache verified (connection_id, ath) bindings to avoid re-verifying proofs on subsequent requests. Each cache entry is small (a connection identifier plus a 32-byte hash), so even connections carrying hundreds of tokens have modest memory requirements. Caching is an optimization, not a requirement: if the server does not cache (or evicts entries under memory pressure), it simply falls back to full JWT signature verification on every request — equivalent to the per-request cost of DPoP.¶
When the client presents the access token to a resource server:¶
The client sends the HTTP request with:¶
The resource server performs the following verifications:¶
a. Standard mTLS verification: Verifies the client certificate as part of the TLS handshake.
b. Token validation: Validates the access token (signature, expiration, audience, etc.).
c. Certificate binding: Verifies that the x5t#S256 in the token's cnf claim matches the presented client certificate.
d. TLS binding required: Checks that cnf.tls_exp is present and a Session-Binding Proof is present.
e. Proof signature: Verifies the proof JWT signature against the public key in the client certificate.
f. Exporter match: Compares the ekm claim in the proof against the TLS Exporter value for the current connection and confirms they match.
g. Token hash: Computes SHA-256 of the presented access token and confirms it matches the ath claim.
h. Timestamp: Confirms iat is within an acceptable window.
i. Per-request claims (when present): Confirms htm and htu match the actual request, and jti has not been seen before.¶
The resource server MUST ensure that steps (a) through (i) are satisfied for every request. This is critical: an attacker who obtains a stolen token and proof may attempt to inject them on their own mTLS connection to the same resource server. Verification ensures the proof signature is checked against the certificate from this connection's handshake, which will not match the attacker's certificate.¶
To satisfy this requirement efficiently, the resource server MAY cache verified bindings keyed on (connection_id, ath). On subsequent requests with the same token on the same connection, a cache hit confirms the binding was already verified; a cache miss (different connection, different token, or first presentation) triggers full verification. This cache is inherently per-connection, so a stolen proof presented on a different connection will always miss and fail full verification. When a TLS connection terminates, the resource server SHOULD purge all cache entries associated with that connection, as the bindings are no longer valid.¶
If all verifications succeed, the resource server processes the request. If any verification fails, the resource server MUST reject the request as specified in Section 3.5.¶
When token introspection [RFC7662] is used, the introspection response MUST include the cnf claim with the tls_exp field. This allows resource servers that do not have direct access to the token's claims (e.g., opaque tokens) to determine whether session binding is required.¶
When verification of the Session-Binding Proof fails or the proof is missing, the resource server MUST respond with HTTP 401 and include a WWW-Authenticate header with the appropriate error code:¶
WWW-Authenticate: Bearer error="invalid_proof",
error_description="Session-Binding Proof verification failed:
exporter mismatch"
¶
WWW-Authenticate: Bearer error="use_session_binding", error_description="This token requires a Session-Binding Proof"¶
The following error code values are defined:¶
iat, jti reuse, ath mismatch, and htm/htu mismatch.¶
cnf.tls_exp claim is present) but no Session-Binding-Proof header was provided. This error signals to the client that it must construct and present a proof.¶
The resource server SHOULD include an error_description parameter with a human-readable explanation of the specific verification failure.¶
This specification extends RFC 8705 by adding session-level binding on top of certificate binding. The x5t#S256 claim from RFC 8705 is reused. Deployments MAY support both mechanisms simultaneously: RFC 8705 provides certificate binding, while this specification adds per-connection session binding.¶
DPoP and this specification address similar goals (proof-of-possession) but differ in binding targets, key management, per-request cost, and the provenance of the binding key:¶
Binding target: DPoP binds tokens to an ephemeral application-layer key. This specification binds tokens to both the client identity (X.509 certificate) and the specific TLS connection (Exporter value). An attacker who exfiltrates a DPoP-bound token and the associated DPoP key can replay the token from any network location; an attacker who exfiltrates a session-bound token cannot, because reproducing the TLS Exporter value requires being on the same TLS connection.¶
Key management: DPoP requires generating, storing, and rotating an ephemeral key pair independent of TLS. This specification reuses the mTLS key pair already established during the handshake, eliminating the entire DPoP key lifecycle.¶
Per-request overhead: DPoP requires constructing and signing a new proof JWT for every HTTP request. This specification constructs the proof once per (token, connection) and reuses it across all requests on that connection (when per-request claims are omitted). This is a fundamental efficiency advantage in high-throughput workload-to-workload scenarios.¶
Identity provenance: A DPoP key is self-generated by the client with no provenance beyond the client's own assertion — any process can create one. This specification reuses the mTLS key, which in workload deployments is issued through a verifiable attestation chain. When the mTLS credential is a standards-based workload identity (e.g., a SPIFFE X.509-SVID issued by SPIRE), the key's issuance was conditioned on node attestation followed by workload attestation. The Session-Binding Proof is therefore not merely "possession of a key" but "possession of a key whose issuance was verified against the workload's identity and execution context." See Appendix D for details.¶
Applicability: DPoP is particularly valuable for public clients that cannot use mTLS. This specification is designed for confidential clients and workloads that already use mTLS. The two mechanisms are complementary rather than competing.¶
In summary, for mTLS-capable environments, this specification provides stronger security (connection-level binding vs. key-level binding), lower per-request cost (amortized proof vs. per-request proof), simpler implementation (no separate key management), and — when backed by a workload identity system — proof whose binding key has verifiable provenance through an attestation chain.¶
A natural question is whether RFC 9449 DPoP, when applied per-hop, can provide the same chain-extension property described in Section 6.1.6. The short answer is that per-hop DPoP can provide chain-extension gating at the authorization server, but the gate is structurally weaker than the one this specification provides, in three independent dimensions.¶
This subsection assumes the per-hop DPoP variant of the comparison: each hop holds its own DPoP key pair, and the authorization server issues each successor token with the requesting hop's DPoP key thumbprint in cnf.jkt. (The alternative variant — using the originating client's DPoP key across the entire chain — is not supported by RFC 9449, since DPoP keys are not transferable between hops and there is no defined delegated-signing primitive for DPoP.)¶
Key custody relative to the agent runtime. A DPoP key is an application-layer key, typically generated within the application process and stored alongside the access tokens it binds. In an agent workload, the same prompt-injection or runtime-compromise channels that exfiltrate the access token typically exfiltrate the DPoP key as well, because they share custody. The mTLS private key used to sign a Session-Binding Proof, in contrast, is held by the TLS terminator — in a sidecar deployment that is a separate process from the agent runtime (see Appendix C), and in hardware-backed deployments it is held in a TPM, HSM, or TEE that the agent runtime cannot read. The realistic attack against an agentic workload is "token exfiltrated, binding key not exfiltrated," and the structural strength of the chain-extension gate depends on which of those keys is the binding key.¶
Attestation provenance. A DPoP key is self-generated by the client and carries no provenance beyond the client's own assertion that it holds it. Any process can create a DPoP key. The mTLS private key in workload-identity deployments is issued only after a verifiable attestation chain (see Appendix D). When the authorization server verifies a Session-Binding Proof during chain extension, it is verifying possession of an attestation-anchored key, not a self-generated one. The chain-extension gate inherits the strength of the underlying workload-identity attestation.¶
Per-request cost in multi-hop chains. Per-hop DPoP requires signing a fresh DPoP proof for every request at every hop. In an N-hop chain with M requests per hop, the total signing cost is N × M signatures. This specification amortizes to N signatures (one per hop's outbound mTLS connection), independent of M. For agentic AI workloads with high per-hop request volume, this is a material cost difference.¶
The two mechanisms are not mutually exclusive. DPoP remains the appropriate choice for public clients that cannot use mTLS, and for deployments where the connection-level binding model is not available end-to-end (see Section 5.2). For confidential, mTLS-capable workloads — the primary target of this specification — the session-binding profile provides a structurally stronger chain-extension gate at lower per-request cost.¶
Token Binding [RFC8471][RFC8472][RFC8473] proposed direct TLS session binding for OAuth and HTTP but did not gain traction in industry. This specification addresses the same core problem — preventing cross-connection token replay — while avoiding the specific deployment barriers that Token Binding encountered.¶
No new TLS extension. Token Binding required a dedicated TLS negotiation extension ([RFC8472]) that had to be implemented and enabled by browser vendors and TLS stacks. This specification uses the TLS Exporter mechanism ([RFC5705], [RFC8446] Section 7.5), which is already available in every compliant TLS 1.2, TLS 1.3, and QUIC implementation without any extension or negotiation.¶
Session-scoped binding, not persistent client keys. Token Binding associated tokens with long-lived, client-managed signing keys, creating persistent client identifiers across connections. This specification binds tokens to the TLS Exporter value, which is unique to each connection and exists only for the lifetime of that session. No persistent identifier is created beyond the TLS session itself.¶
Specified for Token Exchange. Token Binding was not defined for use with RFC 8693 Token Exchange delegation chains. This specification is primarily motivated by exactly that scenario: each hop in a multi-hop agentic delegation chain produces a new bearer token, and session binding contains the blast radius of any single compromise to the specific connection on which that token is presented.¶
Explicit deployment scope constraint. Token Binding encountered complexity in proxy and intermediary topologies, leading to additional specifications ([RFC8473]) to handle referred binding over HTTP. This specification avoids that complexity by explicitly restricting the binding guarantee to deployments where the verifier is a direct endpoint of the client TLS connection — either the resource server itself or a co-located sidecar. Forwarding the EKM through a remote intermediary would change the security property from session binding to proxy-attested binding, which is a categorically weaker guarantee. See Section 5 for details.¶
This specification is not applicable to browser-based web clients, as browsers do not currently expose TLS Exporter values to JavaScript. It is targeted at server-to-server, service-mesh, and agentic AI workloads where both endpoints have direct access to the TLS stack.¶
The WIMSE Workload Identity Token (WIT) and Workload Proof Token (WPT) defined in [I-D.ietf-wimse-s2s-protocol] provide a proof-of-possession mechanism for workload-to-workload communication. This specification is compatible with WIMSE and addresses a complementary problem.¶
WIT/WPT and this specification operate at different layers and answer different questions:¶
Neither mechanism alone provides both properties. In a workload-to-workload flow where both are deployed, WIT/WPT establishes and proves the workload's identity at the application layer while this specification ensures the OAuth authorization token is cryptographically bound to the specific connection that workload established. Together they close the complete chain: from attested workload identity to authorized, connection-scoped API access.¶
WPT is a per-request application-layer proof; this specification amortizes the proof to once per (token, connection) pair at the TLS-channel layer. The two mechanisms are not competing alternatives — deployments that require both workload identity proof and connection-level token binding SHOULD use both.¶
The Transitive Attestation profile [I-D.draft-mw-wimse-transitive-attestation] addresses a complementary problem: binding an identity to a verified execution environment ("Proof of Residency"). While this specification binds tokens to a TLS connection to prevent network-level replay, Transitive Attestation binds identities to a hardware-rooted host to prevent credential export. In high-assurance deployments, both mechanisms MAY be combined: Transitive Attestation ensures the token is used from the correct host, and TLS session binding ensures it is used on the correct connection.¶
This specification does not modify the token exchange protocol itself. The authorization server's token exchange endpoint continues to operate as specified in [RFC8693]. The session binding is applied to the resulting access token through the cnf claim. While this specification is applicable to any OAuth 2.0 access token, RFC 8693 Token Exchange is a primary motivator: each hop in a delegation chain produces a new bearer token, and session binding contains the blast radius of any single token compromise in two distinct ways.¶
Replay containment at the resource server. A stolen session-bound token cannot be replayed at a downstream resource server on a different TLS connection. The binding mechanism prevents direct misuse of the stolen token for resource access, as described in Section 6.1 (T1, T4).¶
Chain-extension containment at the authorization server. A stolen session-bound token also cannot be used as a subject_token (or actor_token) in a new RFC 8693 exchange to obtain a successor token, without possession of the binding key. The authorization server, acting as a verifier of the incoming session-bound token, requires a Session-Binding Proof over the AS-to-client connection's EKM, signed by the private key matching the cnf.x5t#S256 thumbprint. An attacker who has exfiltrated only the token (and not the private key) cannot produce that proof. This property gates token renewal and chain extension on binding-key possession, not merely on token possession.¶
The combined effect is that a stolen session-bound token has no useful lifetime beyond its remaining exp on the original TLS connection: it cannot be replayed elsewhere, and it cannot be exchanged for a fresh successor. This is a meaningful strengthening of the baseline OAuth 2.0 model where a stolen bearer token can be presented at any resource server and, if it is exchangeable, can also be used to mint downstream tokens.¶
When this specification is used with actor-chain token exchange, the
Session-Binding Proof for each hop is verified by the recipient of
that hop. For example, if A presents a token to B and B later
exchanges that token for a successor token targeted to C, B (or a
verifier co-located with B) verifies the A-to-B Session-Binding Proof
before using the inbound token as an RFC 8693 subject_token. B then
generates a new Session-Binding Proof for its outbound token on the
B-to-C connection, as specified in Section 2.3.¶
The Authorization Server handling B's exchange request can verify that
the inbound token carries the expected cnf claim (certificate
thumbprint and tls_exp label) and that B is the intended recipient,
and can enforce policy governing successor-token issuance. However,
it cannot independently recompute the prior-hop TLS Exporter value
because it is not an endpoint of the A-to-B connection.¶
Where Authorization-Server-visible evidence of prior-hop presentation is required, deployments MAY require B to submit a signed confirmation that the inbound token and its Session-Binding Proof were verified before chain extension (that is, before a successor token is issued). This confirmation corresponds to the step proof defined in [I-D.draft-mw-oauth-actor-chain] for verified-profile deployments; the two mechanisms are designed to be used together in high-assurance multi-hop architectures.¶
Emerging frameworks for agentic AI authentication, such as [I-D.draft-klrc-aiagent-auth], compose existing standards — WIMSE Workload Proof Tokens (WPT), HTTP Message Signatures, and RFC 8693 Token Exchange — to address the identity and delegation needs of autonomous agents. These frameworks recommend application-layer proof-of-possession mechanisms, of which DPoP [RFC9449] is the most widely deployed example, where the agent signs a per-request JWT with a private key to demonstrate token binding.¶
For direct mTLS deployments, TLS session binding provides stronger protection than application-layer PoP. WPT and DPoP bind the token to a key: an attacker who exfiltrates both the token and the key material can replay the token from any connection. TLS session binding binds the token to the specific TLS connection via the TLS Exporter value, which cannot be reconstructed outside that connection. The per-request cost advantage over DPoP and WPT is described in Section 4.2.¶
The two approaches are complementary. HTTP Message Signatures, as recommended by such frameworks, remain appropriate for flows traversing TLS-terminating intermediaries where EKM derivation is unavailable end-to-end — a scenario outside the scope of this specification (see Section 5.2). Deployments MAY use TLS session binding for direct mTLS connections and HTTP Message Signatures for cross-intermediary legs of the same workflow.¶
This specification requires that the entity verifying the Session-Binding Proof be an endpoint of the TLS connection on which the token is presented, or a sidecar co-located at the same trust boundary as the resource server that terminates that connection directly. In both cases, the verifier derives the EKM from the TLS session it directly participates in, and no EKM forwarding is required.¶
This covers two deployment models:¶
Deployments where TLS is terminated at a remote intermediary — such as a standalone load balancer or API gateway that is not co-located with the resource server — are outside the scope of this specification.¶
In such topologies, the EKM cannot be conveyed to the verifier without introducing a trust dependency on the intermediary's assertion of what the EKM was. This changes the security property from "bound to the TLS session" to "bound to what a proxy claims the TLS session's EKM was" — a weaker and categorically different guarantee that undermines the core security claim of this specification. Allowing EKM to escape the session boundary via an HTTP header would recreate the kind of mediated-trust complexity that contributed to TLS Token Binding's adoption failures [RFC8471][RFC8472][RFC8473].¶
Deployments requiring TLS-terminating intermediaries SHOULD place the TLS termination point and the Session-Binding Proof verifier within the same trust boundary — for example, by running an Envoy-based sidecar as both the TLS endpoint and the proof verifier, co-located with the resource server backend.¶
This specification is directly applicable to QUIC ([RFC9000]) and HTTP/3 transports. QUIC integrates TLS 1.3 into its handshake ([RFC9001]), and TLS Exporter values are available for use with QUIC connections as specified in Section 4.2 of [RFC9001].¶
The following considerations apply when using this specification over QUIC:¶
This section addresses security considerations in addition to those described in the OAuth 2.0 Security Best Current Practice [I-D.ietf-oauth-security-topics].¶
The following table summarizes the threats addressed by this specification, the specific mechanism that mitigates each threat, and whether DPoP provides equivalent protection.¶
ekm claim in the proof is derived from the TLS handshake transcript and is unique per connection. The resource server compares it against its own locally-derived EKM. A replayed proof will fail the EKM comparison.¶
ekm claim does not match the server's locally-derived value.¶
Within the same TLS connection, an attacker with access to the channel (e.g., a compromised middleware component) could observe and replay requests. This risk is mitigated by including the OPTIONAL per-request claims:¶
jti claim, which provides per-proof uniqueness when the server maintains a replay cache.¶
htm and htu claims, which bind the proof to a specific HTTP method and URI.¶
iat validity windows that limit the temporal scope of any replay.¶
Deployments that require intra-connection replay protection SHOULD include per-request claims.¶
If the client's mTLS private key is compromised, the attacker can produce valid proofs. Mitigations include:¶
This specification reuses the client's mTLS private key to sign the Session-Binding Proof JWT. The same key is therefore used for two distinct cryptographic operations: TLS handshake authentication (per [RFC8446]) and JWS signature over the Session-Binding Proof payload (per [RFC7515]).¶
This reuse is safe because the two operations sign structurally disjoint payloads under domain-separated contexts:¶
CertificateVerify, TLS 1.2 client CertificateVerify) sign a transcript-derived structure that begins with a fixed-content prefix and ends with the handshake transcript hash. The signed content is constrained by the TLS message framing and cannot be chosen by an application-layer attacker.¶
typ: tls-binding-proof+jwt, which provides explicit typing per JWT Best Current Practices [RFC8725].¶
The two signed-content structures cannot be confused: a TLS handshake signature input cannot be constructed to match a valid JWS Signing Input with the required typ, and a JWS Signing Input cannot be constructed to match a valid TLS handshake signature prefix. An attacker observing a Session-Binding Proof signature therefore cannot use it to forge a TLS handshake, and an attacker observing a TLS handshake signature cannot use it to forge a Session-Binding Proof.¶
Implementations MUST set the JWS Protected Header typ to tls-binding-proof+jwt and verifiers MUST reject proofs whose Protected Header typ is absent or does not match. This explicit-typing requirement is the domain-separation mechanism on which the safety of key reuse depends.¶
Deployments that wish to avoid cross-protocol key reuse on principle MAY use a separate signing key for the Session-Binding Proof, provided that key is itself bound to the same workload identity through an independent attestation chain. This loses the "no separate key management" property described in Section 3.3.1 but is otherwise compatible with the specification.¶
[Note to RFC Editor: please remove this section before publication, per [RFC7942].]¶
This section records implementation experience with this specification at the time of writing, per the guidance in [RFC7942].¶
An implementation of this specification exists in a service-mesh and agentic AI environment, deploying the Session-Binding Proof verifier as a co-located Envoy sidecar (per Section 5.1 and Appendix C) with workload identity provided by SPIFFE/SPIRE-issued X.509-SVIDs (per Appendix D). The implementation has verified the following protocol-level properties end-to-end:¶
EXPORTER-oauth-tls-session-bound label and a 32-octet output, integrated through the TLS stack of a production-grade L7 proxy.¶
(connection_id, ath) cache; verified cache-hit behavior for repeated requests on the same connection and full re-verification on connection re-establishment.¶
The implementation is operated within an organization that does not currently make the source publicly available. The authors solicit feedback from other implementers and are willing to coordinate interoperability testing with parties developing independent implementations.¶
This specification registers the following confirmation method in the IANA "OAuth Token Confirmation Methods" registry established by [RFC7800]:¶
This specification registers the following client metadata value:¶
This specification registers the following HTTP header fields:¶
The following individuals have contributed to this document:¶
Roman Zabicki
JPMorgan Chase & Co
Email: roman.zabicki@chase.com¶
Jack McCallie
JPMorgan Chase & Co
Email: jack.mccallie@chase.com¶
Jonian Musa
JPMorgan Chase & Co
Email: jonian.musa@chase.com¶
[Note to RFC Editor: please remove this section before publication.]¶
Applicability and Deployment Scope subsection to the Introduction, foregrounding the co-located-verifier requirement so that the relationship to remote TLS termination is clear on first read.¶
iat window guideline in the Session-Binding Proof payload definition (acceptable window SHOULD NOT exceed 300 seconds), and noted that the chosen window bounds the temporal scope of any intra-connection proof reuse when per-request claims are not used.¶
(connection, ath) bindings, and falls back to per-request JWT verification (comparable to DPoP) otherwise. Added a paragraph on cache state sizing and bounded-eviction safety.¶
Cross-Protocol Key Reuse subsection to the Security Considerations, explaining why reusing the mTLS private key for the Session-Binding Proof JWT is safe given the structural disjointness of TLS handshake signatures and JWS Signing Input, and making typ: tls-binding-proof+jwt a normative MUST as the domain-separation mechanism. Offered a deployment-side opt-out (separate signing key) for organizations that wish to avoid cross-protocol reuse on principle.¶
subject_token or actor_token to obtain a successor token without possession of the binding key). Added a corresponding new threat T6 ("Stolen-Token Chain Extension at the Authorization Server") to the Threat Model.¶
Multi-Hop Chain Extension subsection to the DPoP relationship section, addressing the natural objection that per-hop DPoP could provide equivalent chain-extension gating. The subsection acknowledges that per-hop DPoP does provide a chain-extension gate but is structurally weaker than session binding on three independent dimensions: key custody relative to the agent runtime, attestation provenance of the binding key, and per-request cost in multi-hop chains.¶
(connection_id, ath) cache behavior, multi-hop deployment, SVID rotation handling, and cross-connection / cross-host replay rejection.¶
Prior versions are tracked in the IETF datatracker.¶
In agentic AI architectures, a common deployment pattern places a security sidecar (e.g., Envoy, Istio proxy, or a purpose-built agent gateway) alongside each AI agent workload. This appendix describes how TLS-session-bound tokens integrate with this pattern and the resulting security benefits.¶
The following diagram shows the deployment layout. The AI agent and security sidecar run in the same pod or VM. The sidecar holds the mTLS private key, manages all authenticated outbound connections, and maintains a cache of signed proofs. A single mTLS connection to a remote resource server may carry requests on behalf of many different users, each with a distinct access token; the sidecar constructs and signs a proof once per (token, connection) pair and reuses it for subsequent requests with the same token.¶
+----------------------------------------------------+
| Pod / VM |
| |
| +------------------+ +-------------------+ |
| | AI Agent | | Security | |
| | (app logic, | | Sidecar | |
| | LLM runtime) | | (Envoy etc.) | |
| | | | | |
| | Holds: | | Holds: | |
| | - access tokens | | - mTLS private key| |
| | (per user) | | - X.509 cert | |
| | - prompts | | - EKM cache | |
| | - app context | | - proof cache | |
| | | | (per token) | |
| | Does NOT hold: | | Constructs: | |
| | - private keys | | - Binding Proof | |
| +--------+---------+ +--------+----------+ |
| | plaintext HTTP | |
| | (loopback/UDS) | mTLS |
| +---------->-------------+| |
| | |
+-------------------------------------+---------------+
|
v
Remote Resource Server
¶
The following diagram shows how requests flow from the AI agent through the sidecar to the remote resource server. The sidecar transparently adds the Session-Binding Proof and reuses it for subsequent requests with the same token.¶
AI Agent Security Sidecar Resource Server
| | |
| |=== mTLS handshake ======>|
| | |
| | [ONCE PER CONNECTION] |
| | Both sides derive: |
| | EKM = TLS-Exporter( |
| | "EXPORTER-oauth- |
| | tls-session- |
| | bound", "", 32) |
| | |
| (1) HTTP Request 1 | |
| Authorization: | |
| Bearer <token> | |
| (plaintext, local) | |
+---------------------->| |
| | |
| (2) Sidecar (ONCE PER TOKEN, CONN):|
| - Computes ath = SHA256(token) |
| - Constructs proof JWT: |
| { ath, ekm: EKM, iat } |
| - Signs with mTLS private key |
| | |
| | (3) mTLS Request |
| | Authorization: |
| | Bearer <token> |
| | Session-Binding-Proof: |
| | <proof_jwt> |
| +------------------------>|
| | |
| | (4) Server verifies: |
| | 1. sig matches cert |
| | 2. ath matches token |
| | 3. ekm matches EKM |
| | 4. iat in window |
| | (caches binding) |
| | |
| | (5) 200 OK |
| |<------------------------+
| (6) 200 OK | |
|<----------------------+ |
| | |
| (7) HTTP Request 2 | |
| Authorization: | |
| Bearer <token> | |
+---------------------->| |
| | |
| (8) Sidecar reuses proof |
| | |
| | (9) mTLS Request |
| | Authorization: |
| | Bearer <token> |
| | Session-Binding-Proof: |
| | <proof_jwt> (reused) |
| +------------------------>|
| | |
| | (10) Cache hit: |
| | binding verified |
| | |
| | (11) 200 OK |
| |<------------------------+
| (12) 200 OK | |
|<----------------------+ |
| | |
¶
In a typical agentic deployment, Agent A serves multiple users concurrently. Each user's request triggers an on-behalf-of (OBO) token exchange, producing a distinct access token. All outbound requests to Agent B share a single HTTP/2 mTLS connection, multiplexed across streams:¶
Agent A Sidecar Agent B
| | |
| |=== mTLS (one conn) ===>|
| | EKM derived once |
| | |
User 1: | |
|--Token_1-->| |
| |--(stream 1)----------->|
| | Bearer: Token_1 |
| | Proof: {ath_1,ekm} |
| | (signed once) |
| | |
User 2: | |
|--Token_2-->| |
| |--(stream 3)----------->|
| | Bearer: Token_2 |
| | Proof: {ath_2,ekm} |
| | (signed once) |
| | |
User 1 again: | |
|--Token_1-->| |
| |--(stream 5)----------->|
| | Bearer: Token_1 |
| | Proof: reused |
| | |
¶
Each token is signed once when first seen on this connection. Subsequent requests with the same token reuse the cached proof. With N users and M requests per user, the total signing cost is N (one per token) rather than N×M (one per request as with DPoP).¶
This architecture provides defense-in-depth against agentic AI threat vectors:¶
Prompt injection token exfiltration: Even if a compromised LLM exfiltrates an access token via tool calls, log leakage, or side channels, the attacker cannot produce a valid Session-Binding Proof. The mTLS private key resides exclusively in the sidecar process, which is not accessible to the agent's application logic or LLM runtime.¶
Key isolation: The agent never has access to the signing key. The sidecar can enforce hardware-backed key storage (TPM, HSM) independently of the agent's runtime environment.¶
Transparent integration: The agent application code requires no modifications beyond standard OAuth token handling. The session binding is entirely transparent—the sidecar intercepts outbound requests and adds the proof.¶
Centralized policy enforcement: The sidecar can apply additional policy checks (token scoping, rate limiting, destination allowlisting) before constructing the proof, providing a security control plane for agent traffic.¶
Audit boundary: All authenticated outbound traffic passes through the sidecar, providing a natural audit point for logging which tokens were used, to which destinations, and when.¶
This deployment model aligns with the service mesh architecture used in SPIFFE/SPIRE environments, where the sidecar already manages workload identity certificates. When combined with Transitive Attestation [I-D.draft-mw-wimse-transitive-attestation], the sidecar can additionally attest that the agent is running in a verified execution environment while simultaneously binding all tokens to the active TLS connection.¶
The sidecar must access the TLS Exporter value from the mTLS connection it terminates in order to construct the Session-Binding Proof. See Section 2.2 for the general implementation note on TLS Exporter API availability across TLS libraries and frameworks.¶
Sidecars that implement this specification should handle TLS connection lifecycle events to manage the EKM and proof caches correctly. A typical implementation uses a layered architecture:¶
Session-Binding-Proof header into outbound requests.¶
When a TLS connection terminates, all connection-scoped state — including the EKM and all cached proofs for that connection — SHOULD be purged automatically. Frameworks that support connection-scoped storage (e.g., per-connection filter state) provide this cleanup naturally without requiring explicit invalidation logic.¶
When a connection to the upstream resource server is lost and a new one is established, the sidecar derives a fresh EKM from the new handshake and constructs new proofs. Previously cached proofs are invalid because they contain the old connection's EKM.¶
This appendix is non-normative. It describes how this specification integrates with SPIFFE/SPIRE workload identity infrastructure and the additional security properties that result when the mTLS certificate is a SPIFFE X.509-SVID.¶
When the mTLS certificate is an arbitrary X.509 certificate, this specification provides connection-scoped token binding: the token cannot be replayed on a different TLS connection. When the mTLS certificate is a SPIFFE X.509-SVID issued by a SPIRE deployment, the binding acquires a richer semantic property.¶
A SPIFFE X.509-SVID is issued only after a two-stage attestation process:¶
Node attestation: The SPIRE agent running on the node proves to the SPIRE server that the node is what it claims to be (e.g., via cloud provider instance identity documents, TPM attestation, or Kubernetes service account tokens).¶
Workload attestation: The SPIRE agent verifies the properties of the workload process requesting the SVID (e.g., binary hash, Unix UID/GID, Kubernetes pod labels, container image digest) against policies registered in the SPIRE server.¶
The resulting SVID is short-lived (typically between one minute and one hour, depending on deployment policy) and is automatically rotated by the SPIRE agent before expiry.¶
When a Session-Binding Proof is signed by the private key of a SPIFFE X.509-SVID, the resource server's verification implies the following chain:¶
This property — authorization bound to attested workload identity — is not achievable with DPoP, whose key has no provenance beyond self-generation, or with RFC 8705 alone, which binds to a certificate thumbprint but does not constrain how the certificate was issued or how long it has been valid. The combination of this specification with SPIFFE/SPIRE delivers identity-rooted proof of possession: the binding key is not "a key the client generated" but "a key issued to a specific attested workload through a verifiable trust chain."¶
In this variant, the workload process itself obtains the SVID via the SPIFFE Workload API and uses it directly for mTLS connections. The workload also constructs the Session-Binding Proof using the SVID private key.¶
SPIRE Server
|
| (node + workload attestation)
|
SPIRE Agent (node-level)
|
| Workload API (UDS)
|
+--------------------------------------------------+
| Pod / VM |
| |
| +--------------------------------------------+ |
| | Workload (AI Agent / Service) | |
| | | |
| | Holds (via SPIFFE Workload API): | |
| | - X.509-SVID (cert + private key) | |
| | - SVID bundle (trust roots) | |
| | | |
| | Performs: | |
| | - mTLS connection establishment | |
| | - EKM derivation | |
| | - Session-Binding Proof construction | |
| +--------------------------------------------+ |
| | mTLS (SVID-authenticated) |
+--------------------+--------------------------+---+
|
v
Remote Resource Server
¶
The SPIFFE Workload API (typically a Unix domain socket) delivers the current SVID and its private key to the workload process. The workload uses this SVID as its mTLS client certificate and constructs the Session-Binding Proof by signing with the corresponding private key.¶
SVID rotation: When the SPIRE agent rotates the SVID (before expiry), the workload receives the new SVID via the Workload API. The workload SHOULD establish new mTLS connections using the new SVID. Each new connection produces a new EKM; fresh proofs must be constructed for any tokens presented on those connections. Previously cached proofs, which reference the old connection's EKM, remain valid only on connections established with the old SVID.¶
Security note: In this variant, the SVID private key resides in the same process as the agent's application logic and LLM runtime. The key-isolation benefit described in Appendix C does not apply. A workload compromise may expose both the access token and the private key. This variant is appropriate for environments where process-level compromise is not the primary threat model, or where hardware-backed key storage (e.g., PKCS#11, TPM) is enforced at the platform level independently of the application.¶
In this variant, the SPIRE agent delivers the SVID exclusively to the sidecar. The workload process never receives the SVID private key. The sidecar holds the key, establishes all outbound mTLS connections, and constructs Session-Binding Proofs transparently. This is the pattern described in Appendix C, extended to SPIFFE/SPIRE environments.¶
SPIRE Server
|
| (node + workload attestation)
|
SPIRE Agent (node-level)
|
| Workload API (UDS) — SVID delivered to sidecar only
|
+----------------------------------------------------+
| Pod / VM |
| |
| +------------------+ +-------------------+ |
| | AI Agent | | Security | |
| | (app logic, | | Sidecar | |
| | LLM runtime) | | (Envoy + SDS) | |
| | | | | |
| | Does NOT hold: | | Holds (via SDS): | |
| | - SVID | | - X.509-SVID | |
| | - private key | | - SVID private key| |
| | | | - EKM cache | |
| | | | - proof cache | |
| +--------+---------+ +--------+----------+ |
| | plaintext HTTP (loopback/UDS) |
| +------------>-----------+ |
| | mTLS (SVID) |
+------------------------------------+----------------+
|
v
Remote Resource Server
¶
Envoy-based sidecars typically receive SVID updates from the SPIRE agent via Secret Discovery Service (SDS). When the SVID rotates, the sidecar receives the new certificate and key via SDS and uses it for all subsequent outbound connections. The sidecar's connection lifecycle management described in Appendix C applies directly: on SVID rotation, a new mTLS connection to the resource server produces a new EKM, and the sidecar constructs fresh proofs for any tokens presented on that connection. Resource servers caching (connection_id, ath) bindings are unaffected, as each cache entry is keyed on the connection rather than the certificate.¶
SPIFFE X.509-SVIDs and WIMSE Workload Identity Tokens (WITs) serve the same purpose — carrying attested workload identity — through different credential formats. This specification is compatible with both.¶
When WIMSE WIT/WPT is used alongside this specification in a workload-to-workload flow, the two mechanisms address complementary questions at different layers:¶
Together they close the complete chain: workload identity is asserted and proven at the application layer (WIT/WPT), and the OAuth authorization token is cryptographically bound to the specific connection that workload established (this specification). Neither mechanism alone provides both properties.¶