JWT Complete Guide: Structure, Signing & Security Best Practices
JSON Web Tokens (JWTs) have become the de facto standard for stateless authentication in modern web applications and APIs. Understanding how JWTs work — and their security implications — is essential for any developer building authentication systems. This guide covers everything from the token structure to signing algorithms and common security pitfalls.
What Is a JWT?
A JWT (pronounced "jot") is a compact, URL-safe token format defined in RFC 7519. It allows you to transmit claims (pieces of information) between parties as a JSON object that is digitally signed and optionally encrypted.
A JWT looks like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNjE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
It consists of three Base64url-encoded parts separated by dots: Header.Payload.Signature.
JWT Structure
1. Header
The header typically contains two fields: the signing algorithm and the token type.
{
"alg": "HS256",
"typ": "JWT"
}
This JSON is Base64url-encoded to form the first segment of the token.
2. Payload (Claims)
The payload contains the claims — statements about the user and additional metadata. There are three types of claims:
Registered claims (standardized by RFC 7519):
iss(Issuer) — Who issued the tokensub(Subject) — The user the token representsaud(Audience) — The intended recipientexp(Expiration) — When the token expires (Unix timestamp)iat(Issued At) — When the token was creatednbf(Not Before) — Token is not valid before this timejti(JWT ID) — Unique identifier to prevent replay
{
"sub": "user123",
"name": "Alice Johnson",
"email": "alice@example.com",
"role": "admin",
"iat": 1710230400,
"exp": 1710316800
}
3. Signature
The signature verifies that the token hasn't been tampered with. It is computed by signing the encoded header and payload:
// HMAC-SHA256 signature
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
Signing Algorithms
JWT supports multiple signing algorithms:
Symmetric (Shared Secret)
HS256— HMAC with SHA-256 (most common for simple apps)HS384— HMAC with SHA-384HS512— HMAC with SHA-512
Both the issuer and verifier share the same secret key.
Asymmetric (Public/Private Key)
RS256— RSA with SHA-256 (widely used)ES256— ECDSA with P-256 curve (shorter keys, fast)EdDSA— Edwards-curve DSA (modern, fast)
The issuer signs with a private key; anyone can verify with the public key.
Creating JWTs in Code
# Python with PyJWT
import jwt
import datetime
payload = {
"sub": "user123",
"name": "Alice",
"iat": datetime.datetime.now(datetime.timezone.utc),
"exp": datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=24)
}
# Sign with HMAC-SHA256
token = jwt.encode(payload, "your-secret-key", algorithm="HS256")
print(token)
# Verify and decode
decoded = jwt.decode(token, "your-secret-key", algorithms=["HS256"])
print(decoded["name"]) # "Alice"
// Node.js with jsonwebtoken
const jwt = require('jsonwebtoken');
const token = jwt.sign(
{ sub: 'user123', name: 'Alice' },
'your-secret-key',
{ expiresIn: '24h' }
);
// Verify
const decoded = jwt.verify(token, 'your-secret-key');
console.log(decoded.name); // "Alice"
JWT Authentication Flow
- User sends credentials (username/password) to login endpoint
- Server validates credentials and creates a JWT
- Server returns the JWT to the client
- Client stores the JWT (typically in memory or httpOnly cookie)
- Client sends the JWT in the
Authorization: Bearer <token>header with each request - Server validates the JWT signature and checks claims (expiry, audience, etc.)
Security Best Practices
- Always validate the signature — Never trust an unverified token
- Check the
algheader — Reject"alg": "none"tokens. Whitelist allowed algorithms on the server - Set short expiration times — Use
expclaims of 15 minutes to 24 hours. Use refresh tokens for longer sessions - Use strong secrets — For HMAC, use at least 256 bits of entropy. Never use simple strings like "secret"
- Store tokens securely — Prefer httpOnly cookies over localStorage (prevents XSS theft)
- Don't store sensitive data in the payload — The payload is only encoded, not encrypted
- Validate all claims — Check
iss,aud,exp, andnbfon every request - Implement token revocation — Use a deny-list for critical security events (password change, logout)
# Python: secure JWT verification
decoded = jwt.decode(
token,
"your-secret-key",
algorithms=["HS256"], # Whitelist allowed algorithms
options={
"require": ["exp", "iat", "sub"], # Require essential claims
"verify_exp": True,
"verify_iat": True,
},
issuer="https://api.example.com", # Validate issuer
audience="https://app.example.com", # Validate audience
)
🛠️ Decode & Inspect JWT Tokens Online
Open JWT Tool →Further Reading
- Base64 Encoding Explained — How JWT segments are Base64url-encoded
- Hash Functions Explained — Understanding HMAC-SHA256 used in JWT signing
- JSON Formatting Guide — JWT payloads are JSON objects