
In the previous post, we explored how the certificate-based mutual authentication that happens during the TLS handshake can be applied directly to OAuth2 client authentication, a stronger alternative to shared secrets, particularly in system-to-system communication where mTLS is already in use.
This post takes it a step further. We’ll explore how that same client certificate can be used to bind the access token to the client that requested it so that even if someone intercepts or steals the token, they can’t use it. This is the second part of a two-post series on Mutual-TLS Client Authentication and Certificate-Bound Access Tokens (RFC 8705). If you haven’t read the first part, I’d recommend starting there.
Let’s start by making sure we’re clear on OAuth2 token types because the certificate binding mechanism works differently depending on what kind of token you’re dealing with.
Chapter 1: OAuth2 token types
Token format: opaque vs. JWT
There are two main token formats in OAuth2.
- Opaque (reference-type) tokens store the actual authorization details exclusively on the authorization server. The client receives only a reference value — a randomly generated string with no embedded data. This offers a security advantage: if the token is exposed, nothing meaningful can be extracted from it. When a resource server needs to verify the token and retrieve the authorization data, it calls the authorization server’s introspection endpoint. Opaque tokens were the dominant format in OAuth2’s early days and are still widely used in scenarios where token confidentiality is a priority.

- JWT (value-type) tokens embed authorization details and metadata directly in the token itself, encoded in JSON. This allows clients and resource servers to validate and extract token information locally without a round-trip to the authorization server. That’s the key efficiency advantage of JWTs over opaque tokens. In real-world OAuth2 deployments, you won’t typically see plain unsigned JWTs. What you’ll encounter is one of two secured forms:
- JWS (JSON Web Signature) — JWTs digitally signed by the token issuer
- JWE (JSON Web Encryption) — JWTs encrypted by the token issuer

This is what it looks like when the token is decoded.

Token usage: bearer vs. sender-constrained
Now let’s classify tokens by how a client presents them when accessing a protected resource. This classification is where certificate binding becomes relevant.
Bearer tokens work like cash. When you pay with a banknote, the merchant checks whether the note is authentic, but they don’t verify whether it’s actually yours. You could have found it on the street. If you possess it, you can use it. Bearer tokens work the same way: the resource server validates the token itself, but not the party presenting it. They’re widely adopted because of their simplicity, pass the token as an HTTP Authorization header, and you’re done.
Proof-of-Possession (PoP) tokens, also known as sender-constrained tokens, work more like a credit card. Simply presenting the card isn’t enough — you may need to sign for it, and the merchant might verify your identity against the name printed on it. Possession alone doesn’t prove legitimacy. PoP tokens bind the token to something only the legitimate client can prove ownership of. Someone who steals a PoP token can’t use it — they don’t have the private key or certificate needed to prove they’re the authorized holder.

PoP tokens offer a significantly stronger security posture than bearer tokens. The honest reason they’re not yet as widely adopted is implementation complexity, but for high-assurance scenarios, they’re the right choice. This is exactly the direction the industry is moving, especially as AI agents and autonomous systems start accessing APIs at scale.
Chapter 2: Certificate-bound tokens
Now let’s connect the two posts.
In the previous post, we walked through how the mTLS handshake includes a CertificateVerify message where the client signs a hash of the handshake messages using its private key, proving it actually possesses the private key corresponding to its certificate. This is already a built-in proof-of-possession mechanism.
Now you might be wondering: when an OAuth2 client establishes an mTLS connection with the authorization server, it has already demonstrated ownership of its private key during the handshake. Can we use that same proof to bind the access token to the client certificate?
That’s exactly what the second part of Mutual-TLS Client Authentication and Certificate-Bound Access Tokens (RFC 8705) introduces: Certificate-Bound Access Tokens.
How the binding works
A certificate-bound token is an access token that the authorization server binds to the client certificate presented during the mTLS handshake. To use the token, the client must establish an mTLS connection with the resource server using the exact same certificate. The resource server verifies that the certificate associated with the token matches the certificate in the current connection.
Instead of storing or transmitting the full certificate, the binding is done using the certificate fingerprint, a unique, lightweight identifier for the certificate.

In the first post, we covered how fingerprints are calculated. To recap:
- Convert the certificate to DER binary format (from PEM, if needed).
- Apply SHA-256 to the DER binary data.
- Apply Base64 URL encoding to the resulting hash.

Once the authorization server calculates the fingerprint, it establishes the binding in one of two ways depending on the token type:
-
For opaque tokens — the association between the token and the certificate fingerprint is stored in the authorization server’s database. When a resource server needs to validate the token, it calls the introspection endpoint and retrieves the fingerprint from the response.
-
For JWT tokens— the fingerprint is embedded directly in the token using the Confirmation Claim (
cnf), defined in RFC 7800 (Proof-of-Possession Key Semantics for JWTs). RFC 8705 introduces a new JWT confirmation method member:x5t#S256representing the certificate’s SHA-256 fingerprint, Base64 URL-encoded. This allows resource servers to verify the binding locally without contacting the authorization server.

An authorization server signals support for certificate-bound tokens by including tls_client_certificate_bound_access_tokens in its server metadata. A client can request certificate-bound tokens by including this same parameter in its Dynamic Client Registration request.
Validation at the resource server
For JWT tokens:
- Extract the fingerprint from the
cnfclaim (x5t#S256) in the JWT. - Obtain the client certificate from the current mTLS connection.
- Calculate the SHA-256 fingerprint of that certificate using Base64 URL encoding.
- Compare the calculated fingerprint with the one in the
cnfclaim. - If they match, the client is the legitimate token holder. If not, return
invalid_token.

For opaque tokens:
- Call the authorization server’s introspection endpoint with the token.
- Retrieve the certificate fingerprint from the introspection response.
- Obtain the client certificate from the current mTLS connection.
- Calculate the SHA-256 fingerprint of that certificate.
- Compare the calculated fingerprint with the one from the introspection response.
- If they match, the token is valid. If not, return invalid_token.

Chapter 3: Certificate-bound tokens and public clients
One important clarification before we close, because this is where RFC 8705 is frequently misread.
When an mTLS connection is established between an OAuth2 client and the authorization server, the verified certificate can serve as a client authentication method for confidential clients, server-side applications that can securely manage a private key. This was the focus of the first post.
However, a public client such as a browser-based or mobile application can also establish an mTLS connection and complete the handshake. The key point here: the authorization server should not authenticate a public client using mTLS or any other client authentication method. Public clients use mechanisms like PKCE instead, which are better suited for environments where the client cannot securely hold a private key. I’ve covered PKCE in detail in a previous post.
The second important clarification: mTLS client authentication and certificate-bound tokens are independent features. RFC 8705 defines both, but they don’t require each other. Specifically:
-
A client can authenticate using mTLS (Part 1 of this series) but still use bearer tokens — simpler to implement, lower security bar.
-
A public client with no client authentication method can still use certificate-bound tokens, as long as it’s capable of establishing mTLS connections with both the authorization server and the resource server.
This independence is what makes RFC 8705 practical across a wide range of OAuth2 deployment scenarios, you can adopt the pieces that fit your security requirements without being forced into an all-or-nothing approach.
Wrapping up
Across these two posts, we’ve walked through how mTLS can be applied to OAuth2 in two distinct but complementary ways. First, using the certificate-based authentication that happens during the mTLS handshake to authenticate the OAuth2 client itself, a stronger alternative to client_secret. Second, using the client certificate to bind the access token to the presenting client, so that the token is cryptographically tied to its legitimate holder and useless to anyone who intercepts it.
The underlying insight from RFC 8705 is straightforward: mTLS already does a lot of the hard security work during the handshake. The specification essentially says, let’s not throw that away. Let’s reuse it for client authentication and token binding.
mTLS for OAuth2 Client Authentication: A Stronger Alternative to Shared Secrets Learn how to apply TLS handshake–based authentication to meet OAuth 2.0 client authentication requirements