Signed, encrypted, or both
A JWS proves who wrote it; anyone can read it.
Open this lesson in the learning hubKey points
- A normal JWT is a JWS - signed, not encrypted. The payload is base64url, which is encoding rather than protection: anyone holding the token can read every claim.
- That is the single most common misconception about JWTs, and it has real consequences - putting an email, a role list or an internal id in a token publishes it to the client and to anything that logs the header.
- A JWE encrypts the payload so only the intended recipient can read it. It costs key management on both sides and makes debugging considerably harder.
- Nested JWT - sign then encrypt - gives both authenticity and confidentiality. It is used where claims are genuinely sensitive and the token crosses untrusted intermediaries.
- The usual answer is neither: keep sensitive data out of the token. Put an opaque subject in it and look the rest up server-side.
- Remember tokens end up in access logs, browser history via query strings, and error reports. Assume anything in a JWT will eventually be read by someone you did not intend.
Example
# A JWS is READABLE by anyone. Decode it yourself:
$ echo 'eyJzdWIiOiIxMjMiLCJlbWFpbCI6ImFAYi5jb20ifQ' | base64 -d
{"sub":"123","email":"a@b.com"}
# No key needed. The signature stops MODIFICATION, not READING.
# STRUCTURE:
# JWS header.payload.signature 3 parts, readable
# JWE header.key.iv.ciphertext.tag 5 parts, opaque
---
# WHAT NOT TO PUT IN A TOKEN:
#
# email, phone, name PII, and it leaks into every log
# internal database ids tells an attacker your schema
# permission lists large, and stale the moment they change
# anything you would not because you are effectively publishing it
# paste into a public gist
# WHAT A GOOD TOKEN LOOKS LIKE:
{
"iss": "https://auth.example.com",
"sub": "a1b2c3d4-opaque-user-id",
"aud": "orders-api",
"exp": 1754251200,
"iat": 1754250300,
"jti": "unique-token-id",
"scope": "orders.read orders.write"
}
# Small, opaque, and nothing here is a secret.
---
# WHEN A JWE IS ACTUALLY WARRANTED:
# - the token crosses an intermediary you do not control
# - a regulation requires the claims be unreadable in transit AND at rest
# - the claims themselves are the sensitive data and cannot be looked up
#
# Otherwise: shrink the token, look the rest up, and keep debugging easy.
A signed JWT is readable by anyone holding it - the fix is almost never encryption, it is not putting secrets in the token.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the JWT Authentication Course course, and every lesson in it is listed on the JWT Authentication Course contents page.