JWT vs JWE: signed vs encrypted tokens
“JWT” describes the claims format. A JWT is carried either as a JWS (signed, readable by anyone) or as a JWE (encrypted, readable only by the recipient). Most tokens you meet are JWS.
Telling them apart
| Signed JWT (JWS) | Encrypted JWT (JWE) | |
|---|---|---|
| Segments | 3 — header.payload.signature | 5 — header.key.iv.ciphertext.tag |
| Header | alg (e.g. RS256) | alg (e.g. RSA-OAEP-256) + enc (e.g. A256GCM) |
| Payload readable? | Yes, by anyone | Only with the recipient's key |
| Protects | Integrity & authenticity | Confidentiality (and integrity via AEAD) |
| Spec | RFC 7515 | RFC 7516 |
JWS: integrity
A signed JWT proves who issued it and that nobody changed it. It does not hide anything: the payload is merely Base64URL-encoded. Anyone who intercepts the token — or the user who holds it — can read every claim.
JWE: confidentiality
A JWE encrypts the payload for a specific recipient. Paste one into the decoder and it is detected as Encrypted JWT: the protected header (alg, enc, kid) is shown, but the claims cannot be decoded without the private or shared key — and JWTDecoder does not pretend otherwise.
eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0 ← protected header
.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgck… ← encrypted key
.48V1_ALb6US04U3b ← IV
.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_E… ← ciphertext
.XFBoMYUZodetZdvTiFvSkQ ← authentication tagNested: sign then encrypt
When you need both, sign the claims (JWS) and then encrypt the signed token (JWE) with cty: "JWT". The recipient decrypts, then verifies the inner signature. If you paste a nested JWS whose payload is another JWT, the decoder offers to decode the inner token.
Paste any JOSE value — the decoder tells you whether it is a JWS, a JWE, or malformed, and why.
Identify a token