What does an OAuth 2.0 authorization server put in the `cnf` claim of an access token it binds to a client key?
answer
- the token names a key
- a digest, never the key itself
- one member per binding mechanism
- jkt hashes the JWK canonically
- x5t#S256 hashes the DER certificate
basics
~20 sThe cnf claim carries a digest identifying the key, not the key itself: jkt is the base64url-encoded RFC 7638 JWK SHA-256 Thumbprint of the client's public key, and x5t#S256 is the base64url SHA-256 hash of the client certificate's DER encoding.
solid answer
~40 sA key-bound access token carries a confirmation claim, `cnf`, naming the key whose holder may legitimately present it. Under DPoP (RFC 9449 §6.1) the member is `jkt`: the authorization server takes the public key from the `jwk` header of the DPoP proof sent to the token endpoint, computes its RFC 7638 JWK SHA-256 Thumbprint and base64url-encodes it. Under mutual-TLS binding (RFC 8705 §3.1) the member is `x5t#S256`: the base64url-encoded SHA-256 hash of the DER encoding of the certificate the client presented — the certificate itself, not its public key — and the specification states that JWT representation as a SHOULD, not a MUST. Either way the claim holds a digest, so a verifier recomputes it from the credential actually used and compares.
code
json · 11 lines{
"iss": "https://as.example.org",
"sub": "e3d2c1b0-7f45-4a19-9a0e-2c7b18d4f6a1",
"aud": "https://api.example.org",
"iat": 1758286800,
"exp": 1758290400,
"scope": "commissioning:write",
"cnf": {
"jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I"
}
}go deeper
Recall that a key-bound access token names a key inside itself, in the cnf claim, and that what sits there is a hash identifying the key or certificate rather than any secret material.
Be able to say which member goes with which mechanism and how each value is derived: jkt from an RFC 7638 JWK Thumbprint of the public key, x5t#S256 from a SHA-256 over the certificate's DER bytes.
Show that the binding is worth only as much as the comparison downstream, and that renewing a certificate around an unchanged key breaks an x5t#S256 binding while a jkt binding is untouched.
Weigh the cost before mandating key binding across an estate: every consumer must recompute digests, and the two forms move the burden between certificate lifecycle management and client-side key storage.
## What a confirmation claim is An ordinary OAuth 2.0 access token is honoured on presentation alone: it names a subject, an audience, a lifetime and a scope, but nothing about **who** may present it. A key-bound access token adds exactly that, at the moment of issuance, in the **confirmation claim** `cnf`. The claim's value is a JSON object holding one member for the binding mechanism in play, and in both mechanisms this leaf covers that member is a **digest** — a fixed-length hash identifying a key or a certificate without carrying either. The authorization server mints it. The client does not ask for a particular value, and anything that later validates the token recomputes the digest from the credential the presenter actually used and compares the two strings. ## `cnf.jkt`, the DPoP form Under DPoP (RFC 9449 §6.1) the client generates an asymmetric key pair and signs a small JWT — the proof — whose JOSE header carries a `typ` of `dpop+jwt` and the **public** key in a `jwk` member. When that proof accompanies the request to the authorization server's token endpoint, the server: 1. validates the proof — the signature verifies under the key in its own header, `htm` matches the request method, `htu` matches the request URI without query or fragment, `iat` is recent, and `jti` has not been seen before; 2. computes the **JWK SHA-256 Thumbprint** of that public key, the canonical digest defined by RFC 7638; 3. base64url-encodes the 32-byte digest, giving a 43-character string, and mints it into the issued access token as the `cnf` member `jkt`. Because RFC 7638 canonicalises the key before hashing it, every party derives the same value from the same key without coordinating: the client, the authorization server and any consumer of the token all arrive at one string. ## `cnf.x5t#S256`, the mutual-TLS form RFC 8705 §3 binds a token to an X.509 certificate rather than to a bare key. The client presents a certificate on the mutual-TLS connection it uses to reach the token endpoint, and the authorization server records the **base64url-encoded SHA-256 hash of the DER encoding of that certificate** as the `cnf` member `x5t#S256` (§3.1). Three points are regularly stated wrongly: - the digest covers the **whole certificate**, not its public key, so re-issuing an unchanged key inside a fresh certificate produces a different value and breaks the binding; - the specification states that JWT representation as a **SHOULD**, not a MUST, which is part of why the introspection representation in §3.2 exists at all; - binding under §3 is independent of client authentication under §2 — the server binds to whatever certificate the client presented, so a client that authenticated by some other means, including one with no credential of its own, can still be issued a certificate-bound token. ## The two forms side by side | | `cnf.jkt` | `cnf.x5t#S256` | |---|---|---| | digest is taken over | the public key, canonicalised as a JWK | the DER encoding of the X.509 certificate | | hash and encoding | SHA-256, base64url | SHA-256, base64url | | defined by | RFC 9449 §6.1 | RFC 8705 §3.1 | | what the presenter later demonstrates | it can sign with the matching private key | it holds that certificate on a mutual-TLS connection | | effect of certificate renewal | no certificate is involved | the value changes unless the bytes are identical | | stated requirement level in a JWT | the DPoP representation | SHOULD | ## Where the same claim appears again The token is not the only place the binding is published. RFC 9449 §6.2 and RFC 8705 §3.2 both put the same `cnf` member into the authorization server's introspection response for that token, so a party that cannot read the token's own claims still learns which key its presenter must prove it holds. The authorization server also advertises whether it does any of this, in its metadata document: `dpop_signing_alg_values_supported` lists the JWS algorithms it accepts for proofs, and `tls_client_certificate_bound_access_tokens` says whether it issues certificate-bound tokens, alongside `mtls_endpoint_aliases` naming the endpoints reached over a mutual-TLS connection. ## What the claim does not do - It does not hide or protect the token. A key-bound access token is still a document that can be read and copied. - It does not replace the ordinary checks on the token — signature, `iss`, `aud`, `exp` — it is one more check beside them. - It does not place key material in the token. Both members are digests, so whatever stores them stores no secret. - It does nothing on its own. The binding bites only where something recomputes the digest from the credential in front of it and compares. An issuer that mints `cnf` into every token while no consumer ever compares it has bought a longer token and nothing else.
- Where else does an authorization server publish the same `cnf` member for a token it has issued?In the introspection response for that token. RFC 9449 §6.2 and RFC 8705 §3.2 both place the same `cnf` member in the JSON the authorization server returns, so a party that cannot read a self-contained token's claims still learns which key or certificate the presenter must hold.
- Does `cnf.x5t#S256` hash the certificate's public key, the way `jkt` hashes a JWK?No. `jkt` is an RFC 7638 JWK Thumbprint of the public key. `x5t#S256` is a SHA-256 hash of the DER encoding of the entire X.509 certificate, so re-issuing the very same key pair inside a new certificate yields a different value and invalidates the binding.
- May an authorization server issue a certificate-bound access token to a client that authenticated some other way?Yes. RFC 8705 §3 binding is independent of §2 client authentication: the server binds to whatever certificate the client presented on the mutual-TLS connection. A client that authenticated by another method, or that holds no client credential at all, can still receive a certificate-bound token.
A warehouse pass stamped with the serial number of one particular van. The pass is readable by anyone, but the gate reads the number off it and checks it against the vehicle actually at the barrier.
saying these in an interview costs you the question
- Says the jkt member carries the public key rather than a digest of it
- Treats x5t#S256 as a hash of the certificate's public key
- Thinks the authorization server generates the key and sends it to the client
- Assumes a cnf claim removes the need to check signature, audience and expiry
- Believes a certificate-bound token requires the client to have authenticated with that certificate
- Claims the thumbprint is hex-encoded in the claim