| Internet-Draft | TLS-Session-Bound-Tokens | March 2026 |
| Krishnan, et al. | Expires 21 September 2026 | [Page] |
This document defines a mechanism for binding OAuth 2.0 access tokens to a specific mutual TLS (mTLS) session. The binding is achieved through a proof token that incorporates the TLS Exporter value [RFC5705] 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. While applicable to any OAuth 2.0 access token presented over mTLS, this specification is primarily motivated by the Token Exchange protocol [RFC8693], 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 21 September 2026.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
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 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. This document addresses this gap by binding access tokens to the mTLS connection on which they are presented.¶
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]. TLS 1.3 is RECOMMENDED because TLS 1.2 abbreviated handshakes (session resumption) may reuse the same master secret across connections, weakening the binding.¶
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.¶
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"
}
¶
The payload claims are defined as follows:¶
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 a significant advantage over DPoP:¶
(connection, ath) binding and reduce subsequent verifications to a cache lookup. DPoP requires full JWT verification per request.¶
When OPTIONAL per-request claims (jti, htm, htu) are included, the proof must be constructed per request (similar to DPoP). 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.¶
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, and per-request cost:¶
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.¶
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), and simpler implementation (no separate key management).¶
The WIMSE Workload Identity Token (WIT) and Workload Proof Token (WPT) defined in [I-D.ietf-wimse-s2s-protocol] provide a similar proof-of-possession mechanism for workload-to-workload communication. This specification is compatible with WIMSE and can be used in conjunction with WIT/WPT when mTLS-based session binding is required for tokens obtained via RFC 8693 exchange.¶
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 to the specific TLS connection on which it is presented.¶
In deployments where TLS is terminated at the application server (pass-through mode), this specification works without modification. Both the client and server have direct access to the TLS Exporter value.¶
When a TLS-terminating proxy (e.g., a load balancer or API gateway) sits between the client and the resource server, the proxy MUST forward the following information to the backend:¶
A new HTTP header is defined for conveying the exporter value:¶
TLS-Exporter: <base64url-encoded exporter value>¶
The proxy MUST derive the exporter using the label and parameters specified in Section 2.2 from the client-facing TLS session, and forward it in this header over a trusted, integrity-protected connection to the backend.¶
The backend resource server MUST use the forwarded exporter value (instead of its own locally-derived value) when verifying the Session-Binding Proof.¶
Security Warning: The TLS-Exporter header contains security-sensitive material. The connection between the proxy and backend MUST be integrity-protected (e.g., via a separate mTLS connection or a trusted network). The backend MUST NOT accept this header from untrusted sources.¶
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. This risk is mitigated by:¶
The TLS-Exporter header introduces a trust dependency on the proxy. A compromised proxy could forge exporter values. Mitigations include mutual authentication between proxy and backend, and restricting the header to trusted network segments.¶
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:¶
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 and manages all authenticated outbound connections.¶
+----------------------------------------------------+
| Pod / VM |
| |
| +------------------+ +-------------------+ |
| | AI Agent | | Security | |
| | (app logic, | | Sidecar | |
| | LLM runtime) | | (Envoy etc.) | |
| | | | | |
| | Holds: | | Holds: | |
| | - access tokens | | - mTLS private key| |
| | - prompts | | - X.509 cert | |
| | - app context | | - EKM cache | |
| | | | | |
| | 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 | |
|<----------------------+ |
| | |
¶
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.¶