DEV Community

Sven Schuchardt
Sven Schuchardt

Posted on • Originally published at biztechbridge.com

JWT Explained: What's Actually Inside a JSON Web Token

You're integrating an API and you get back a token that starts with eyJ. You paste it somewhere and suddenly you can read your user's email address, their user ID, and an expiry timestamp. No decryption key needed. How? And if anyone can read it, is that secure?

JWTs look encrypted but aren't. That tension — readable but trustworthy — is the whole point. Understanding it takes about five minutes, and it changes how you think about auth tokens for good.

What is a JWT?

A JSON Web Token is three base64url-encoded strings joined by dots:

header.payload.signature
Enter fullscreen mode Exit fullscreen mode

Take a real minimal example:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImV4cCI6MTcxMjcwMDAwMH0.signature
Enter fullscreen mode Exit fullscreen mode

Each part can be decoded in a browser console right now — no keys, no secrets, no libraries:

// Manually decode the payload (works in any browser console)
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsImV4cCI6MTcxMjcwMDAwMH0.signature";
const payload = JSON.parse(atob(token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/')));
console.log(payload);
// { sub: "user_123", email: "user@example.com", exp: 1712700000 }
Enter fullscreen mode Exit fullscreen mode

Part 1 — Header: Contains alg (the signing algorithm, e.g. HS256 or RS256) and typ (always "JWT"). Decoded, it looks like { "alg": "HS256", "typ": "JWT" }.

Part 2 — Payload: The claims — data statements about the user or token. These are just JSON key-value pairs. Standard claim names are short by convention (sub, exp, iat) but the values can be anything. Custom claims like role or org_id are perfectly valid.

Part 3 — Signature: An HMAC or RSA hash of base64url(header) + "." + base64url(payload), computed using a secret known only to the issuer. This is the part that makes the token trustworthy — not readability, but tamper-evidence.

The key insight: JWTs are signed, not encrypted. The payload is readable by anyone who has the token. Only the issuer can produce a valid signature.

Standard Claims

The JWT spec defines a set of registered claim names. You don't have to use them, but you should — they're understood by every JWT library.

Claim Name Meaning
sub Subject User identifier (user ID, email, etc.)
iss Issuer Who created the token (your auth server)
aud Audience Who the token is intended for
exp Expiration Unix timestamp when token expires
iat Issued at Unix timestamp when token was created
nbf Not before Token not valid before this timestamp
jti JWT ID Unique token identifier (for revocation)

exp and iat are Unix timestamps — seconds since January 1 1970. An exp of 1712700000 means the token expires at a specific calendar date and time. Paste any JWT into our JWT decoder tool to see the header, payload, and claims broken out — without sending the token to any server.

Why it Works — and Where it Doesn't

The signature prevents tampering. If you change even one byte of the payload, the signature becomes invalid. The server verifies by re-computing the signature with its own secret and comparing. If they match, the payload hasn't been touched since the issuer signed it.

But the payload is public. Everyone who holds the token can read it. That means:

  • Never put passwords, credit card numbers, or API secrets in a JWT payload.
  • Never put anything you wouldn't put in a cookie you're okay with users reading.
  • Session tokens and user IDs are fine. Sensitive personal data should stay server-side.

A common mistake in early JWT implementations: accepting a token as proof of identity without verifying the signature. A token that decodes to { "sub": "admin" } proves nothing on its own — the signature is what proves it came from your auth server. Always verify server-side before trusting any claim.

Further Reading

Top comments (0)