Understanding JWT tokens

6 min read

JSON Web Tokens (JWTs) are everywhere in modern authentication — the string your app gets after logging in and sends with every request afterwards. They look opaque, but the format is straightforward once you break it into its three parts.

The three parts of a JWT

A JWT is three chunks separated by dots: header.payload.signature. Each chunk before the signature is just Base64url-encoded JSON, which is why our JWT Decoder can read one instantly.

  • Header — metadata, chiefly the signing algorithm (e.g. HS256 or RS256) and token type.
  • Payload — the claims: who the user is, when the token was issued and when it expires, who issued it, and any custom data your app adds.
  • Signature — a cryptographic seal, computed over the header and payload, that proves the token hasn’t been altered.

How the signature keeps a token honest

The signature is what makes a JWT trustworthy. When the server issues a token, it signs the header and payload with a secret key (or a private key). When the token comes back, the server recomputes the signature and checks it matches. Change even one character of the payload — say, bumping your user ID to an admin’s — and the signature no longer matches, so the server rejects it. The security rests entirely on this check and on keeping the signing key secret.

The misconception that causes real bugs

Here’s the part that surprises people: the payload is not encrypted. It’s only Base64url-encoded, which is trivially reversible — paste any token into the decoder and you’ll see its contents in plain JSON. Two consequences follow. First, never put secrets (passwords, private data) in a JWT payload, because anyone holding the token can read them. Second, a JWT protects integrity (it can’t be tampered with) but not confidentiality (it can be read). Treat the whole token as a credential and keep it out of logs and URLs.

Verifying vs. decoding

It’s important to separate two operations. Decoding a JWT — what a browser tool does — just reads the header and payload; it does not and cannot confirm the token is genuine. Verifying a JWT means recomputing the signature with the secret key, which must happen on your server. A common vulnerability is trusting a token’s claims after only decoding it. Always verify the signature server-side before you act on what a token says, and check that it hasn’t expired.

Practical tips

  • Keep expiry times short and use refresh tokens for long sessions.
  • Store tokens carefully on the client; anyone with the token can act as the user until it expires.
  • When debugging an auth problem, decode the token first to confirm the claims and expiry are what you expect.

Once you can read the three parts, JWTs stop being a black box. Inspect one for yourself with the JWT Decoder — it runs entirely in your browser, so even a live token stays on your device.

Tools in this guide