A signed credential like a JSON Web Token (JWT) is normally all-or-nothing. The issuer signs a bundle of claims (name, birthdate, address, document number) and the signature covers the whole bundle. To prove any one claim, you present the whole token, because removing a field would break the signature. That is fine for a server checking its own session token. It is a privacy problem when the token is your government identity and the verifier is a website that only needed to confirm you are an adult.
SD-JWT, short for Selective Disclosure for JWTs, is an IETF specification developed in the OAuth working group to fix exactly this. It lets the issuer sign a credential once, and lets you, the holder, choose at presentation time which individual claims to reveal and which to keep hidden, without invalidating the signature. It is a foundational building block for the digital identity wallets now being deployed, including the European Union's digital identity framework.
The trick: sign the hashes, not the values
The mechanism is simpler than it sounds, and it rests on the one-way property of a cryptographic hash. When the issuer creates the credential, it does not put your birthdate directly into the signed token. Instead, for each disclosable claim it does the following:
- Generates a random salt for that claim.
- Builds a small structure of [salt, claim name, claim value], for example [random salt, "birthdate", "1990-04-12"].
- Hashes that structure and places only the hash digest into the signed JWT.
The signed token, then, contains a list of digests rather than the raw values. Alongside the token, the issuer gives you the full set of plaintext structures, called disclosures: the salt, name, and value for each claim. You hold both pieces. The signed token is fixed and unchangeable. The disclosures are separable.
When you present the credential to a verifier, you send the signed JWT plus only the disclosures you choose to reveal. To prove you are over the drinking age, you might send only the disclosure for an age_over_21 claim. The verifier hashes that disclosure, finds the matching digest inside the issuer-signed token, and confirms it. For every claim you withheld, the verifier sees only an opaque digest with no way to reverse it into a value. The signature still checks out, because you removed disclosures, not signed content.
Without a random salt, a verifier could guess a claim's value and confirm it by hashing the guess. "Is this person's country France?" is a short list to brute-force. The per-claim salt makes the hidden digests resistant to that guessing, so an undisclosed claim stays genuinely undisclosed.
Stopping someone from using your credential
Selective disclosure alone would let anyone who intercepted your presentation replay it. SD-JWT addresses this with optional key binding. At issuance, the credential is tied to a public key whose private half lives in your wallet, often in a secure element. At presentation, you add a small signed object, a key-binding JWT, proving you control that private key and binding the presentation to this specific verifier and moment. A stolen copy of your disclosures is then useless to a thief, because they cannot produce that proof of possession.
What SD-JWT does and does not hide
| Protects against | Does not protect against |
|---|---|
| The verifier learning claims you did not disclose | Two verifiers comparing notes to correlate your presentations |
| A thief replaying your credential (with key binding) | The issuer learning where you used the credential, in some deployment models |
| Tampering with any claim (the signature covers all digests) | Full unlinkability across uses of the same credential |
That last row is the honest limit, and it is worth stating plainly because the marketing around digital identity tends to gloss over it. Every time you present the same SD-JWT credential, you send the same issuer signature. That signature is a stable, unique value. If two different verifiers, or one verifier across two visits, record what they received, the repeated signature lets them link those sessions as the same credential, even though the disclosed claims differed. SD-JWT gives you selective disclosure, not unlinkable disclosure.
There are mitigations. The common one is batch issuance: the issuer hands you many single-use copies of the credential, each with a fresh signature, so you can present a different one each time and avoid the repeating value. It works, but it is operational overhead, and it does not match the cleaner guarantee of approaches built on zero-knowledge proofs or BBS signatures, which can prove a claim without ever revealing a reusable correlator. SD-JWT's design bet is that a hash-and-salt scheme built on widely deployed signatures is simpler to implement and audit today, and that the linkability gap is acceptable for many uses or closeable with batch issuance. Whether that bet holds is one of the live debates in the identity-wallet world.
Why this belongs in the privacy toolkit
Digital identity is arriving whether or not the privacy questions are settled, and the difference between a wallet that practices data minimization and one that hands over your whole identity document on every request is exactly the difference SD-JWT and its alternatives are trying to make real. The principle underneath is one we hold to across everything we build: a system should ask for, and reveal, the minimum a transaction actually requires. Proving you are old enough to buy a drink should cost the bartender one bit of information, not your address.
SD-JWT is not the final word, and it is not pretending to be. It is a deployable step toward credentials that reveal one fact at a time, with a clearly stated boundary on where its privacy stops. Knowing that boundary is what lets you judge when it is enough and when you should be asking for something stronger.