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.
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?+
What's the difference between JWT and session tokens?+
Should I store JWTs in localStorage or cookies?+
What causes 'JWT expired' errors?+
🔧 Free Tools Used in This Guide
FreeToolKit Team
FreeToolKit Team
We build free browser-based tools and write practical guides that skip the fluff.
Tags: