🔑Developer

JWT Tokens Explained (Without the Buzzwords)

What JWTs actually are, how they work, what's inside them, and the security mistakes most tutorials skip over. Plain English for developers.

8 min readSeptember 25, 2025By FreeToolKit TeamFree to read

JWT (JSON Web Token) is one of those technologies that seems simple until you try to implement it securely. Most tutorials cover the happy path. This one covers what they leave out.

The Three Parts of a JWT

A JWT looks like three Base64URL-encoded strings separated by dots: xxxxx.yyyyy.zzzzz

  • Header (xxxxx): Algorithm and token type. Typically {"alg":"HS256","typ":"JWT"}.
  • Payload (yyyyy): The claims — user data, permissions, expiry time. Anyone can read this.
  • Signature (zzzzz): HMAC-SHA256 (or RS256, etc.) of the header + payload using your secret. Only your server can create a valid signature.

The signature is what makes JWTs trustworthy. You can verify the token was created by your server and hasn't been tampered with, without a database lookup.

Standard Claims You'll See in JWTs

  • sub (subject): Who the token is about — usually a user ID. '"sub": "user_123"'
  • iss (issuer): Who issued the token — usually your auth server's domain.
  • exp (expiration): Unix timestamp when the token expires. Always set this.
  • iat (issued at): When the token was created.
  • aud (audience): Who the token is for. Prevents using a token meant for service A at service B.

The Security Mistakes Tutorials Skip

Here's what trips people up in production:

  • Not validating the 'alg' header: Some libraries historically allowed setting alg:none in the header, bypassing signature verification entirely. Always hardcode the expected algorithm on the server, never trust the header's alg claim.
  • Weak secrets: A 6-character HMAC secret can be brute-forced. Use at least 256-bit (32 character) random secrets. Don't use your app name or a dictionary word.
  • Tokens that never expire: JWTs can't be invalidated mid-session without additional infrastructure. If a token has no expiry and gets stolen, that's permanent access. Set reasonable expiry times — 15 minutes to 1 hour for access tokens.
  • Putting sensitive data in the payload: The payload is readable by anyone who has the token. Don't put passwords, credit card numbers, or PII in JWT claims.
  • Storing in localStorage: Vulnerable to XSS. Use HttpOnly cookies.

Access Tokens vs Refresh Tokens

The pattern that makes JWT expiry practical: short-lived access tokens (15–60 minutes) paired with long-lived refresh tokens (7–30 days). The access token is used for API requests. When it expires, the refresh token is used to silently get a new access token. The refresh token is stored in an HttpOnly cookie and is the one you can invalidate server-side by deleting it from a database.

Should You Build Your Own JWT Auth?

Probably not. The details that bite teams — token rotation, revocation lists, key rotation, algorithm confusion attacks — are solved problems in libraries like Auth0, Clerk, NextAuth, or Supabase Auth. JWT is a good thing to understand; building a production auth system from scratch is a different proposition.

Frequently Asked Questions

Can I decode a JWT without the secret key?+
Yes — the header and payload are just Base64URL-encoded and can be decoded by anyone. Only signature verification requires the secret key. This is by design: JWTs are meant to be readable (clients can inspect their own claims) but tamper-proof (you can't modify the payload without the secret to re-sign it). Don't put sensitive information in the payload if you don't trust the client.
What's the difference between JWT and session tokens?+
Session tokens are opaque references — the server stores the session data and the token is just a lookup key. JWTs are self-contained — all claims are in the token itself, so the server doesn't need to store anything. JWTs are stateless (good for horizontal scaling) but can't be truly revoked before expiry without additional infrastructure. Session tokens are stateful but revocation is instant.
Should I store JWTs in localStorage or cookies?+
Cookies (HttpOnly, Secure, SameSite=Strict) are safer. LocalStorage is accessible to any JavaScript running on the page — including injected scripts from XSS attacks. An HttpOnly cookie can't be read by JavaScript at all, which eliminates a major attack vector. The security-first recommendation is always HttpOnly cookies for tokens.
What causes 'JWT expired' errors?+
The token's 'exp' (expiration) claim timestamp has passed. JWTs contain an expiry time set by the server when they're issued. When that time passes, the token is rejected. Fix: implement refresh token logic to get a new access token before the current one expires, or handle the 401 response by redirecting to login.

🔧 Free Tools Used in This Guide

FT

FreeToolKit Team

FreeToolKit Team

We build free browser-based tools and write practical guides that skip the fluff.

Tags:

jwtauthenticationsecuritydeveloper