JWT Tokens Explained: Structure, Use Cases & Security Best Practices
Everything you need to know about JSON Web Tokens — from basic structure to production security patterns.
What is a JSON Web Token (JWT)?
JSON Web Token (JWT, pronounced "jot") is an open standard (RFC 7519) that defines a compact and self-contained way to securely transmit information between parties as a JSON object. The information can be verified and trusted because it is digitally signed using a secret (with the HMAC algorithm) or a public/private key pair (using RSA or ECDSA).
JWTs have become the de facto standard for authentication in modern web applications, particularly in single-page applications (SPAs), mobile apps, and microservices architectures. They are used by virtually every major platform — Google, Facebook, GitHub, AWS, and countless others use JWTs for their authentication systems.
The key advantage of JWTs is that they are stateless — the server does not need to store session data. All the information needed to authenticate a user is contained within the token itself. This makes JWTs ideal for distributed systems where multiple servers need to verify authentication without sharing session state.
JWT Structure
A JWT consists of three parts separated by dots (.):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c1. Header
The header typically consists of two parts: the type of token (JWT) and the signing algorithm (e.g., HMAC SHA256 or RSA).
{
"alg": "HS256",
"typ": "JWT"
}2. Payload
The payload contains claims — statements about the user and additional metadata. There are three types of claims: registered, public, and private.
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"exp": 1516242622
}3. Signature
The signature is created by taking the encoded header, encoded payload, a secret, and the algorithm specified in the header, then signing them.
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)Registered Claims
The JWT specification defines several registered claims — predefined claim names that are recommended (but not required):
| Claim | Name | Description |
|---|---|---|
| iss | Issuer | Who issued the token |
| sub | Subject | Who the token is about (usually user ID) |
| aud | Audience | Intended recipient of the token |
| exp | Expiration | When the token expires (Unix timestamp) |
| nbf | Not Before | Token is not valid before this time |
| iat | Issued At | When the token was created |
| jti | JWT ID | Unique identifier for the token |
How JWT Authentication Works
Here is the typical JWT authentication flow:
User Login
User sends credentials (email/password) to the authentication server.
Server Validates
Server verifies credentials against the database. If valid, creates a JWT with user claims.
Token Returned
Server sends the JWT back to the client (in response body or Set-Cookie header).
Client Stores Token
Client stores the JWT (in HttpOnly cookie or memory — avoid localStorage for sensitive tokens).
Authenticated Requests
Client includes the JWT in the Authorization header: Bearer <token> for subsequent API requests.
Server Verifies
Server verifies the JWT signature and checks claims (expiration, issuer, etc.) before processing the request.
JWT Implementation in Node.js
import jwt from "jsonwebtoken";
const SECRET = process.env.JWT_SECRET; // Use a strong, random secret
// Create a token
function createToken(user) {
return jwt.sign(
{
sub: user.id,
email: user.email,
role: user.role,
},
SECRET,
{
expiresIn: "15m", // Short-lived access token
issuer: "myapp.com",
audience: "myapp.com",
}
);
}
// Verify a token
function verifyToken(token) {
try {
const decoded = jwt.verify(token, SECRET, {
issuer: "myapp.com",
audience: "myapp.com",
});
return { valid: true, decoded };
} catch (error) {
return { valid: false, error: error.message };
}
}
// Express middleware
function authMiddleware(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith("Bearer ")) {
return res.status(401).json({ error: "No token provided" });
}
const token = authHeader.split(" ")[1];
const { valid, decoded, error } = verifyToken(token);
if (!valid) {
return res.status(401).json({ error: "Invalid token", details: error });
}
req.user = decoded;
next();
}JWT Use Cases
- Authentication — The most common use case. After login, each subsequent request includes the JWT, allowing access to routes, services, and resources permitted for that user.
- Authorization — JWTs can contain role and permission claims, enabling fine-grained access control without additional database queries.
- Information Exchange — JWTs can securely transmit information between parties. The signature ensures the sender is who they claim to be and the content has not been altered.
- Single Sign-On (SSO) — JWTs are widely used in SSO implementations because of their small overhead and ability to be used across different domains.
- API Authentication — Stateless JWT authentication is ideal for RESTful APIs, especially in microservices architectures where services need to independently verify requests.
- Password Reset Tokens — Short-lived JWTs can serve as secure password reset links with built-in expiration.
JWT Security Best Practices
⚠️ Critical Security Rules
- Never store sensitive data (passwords, credit cards) in JWT payload — it is only encoded, not encrypted
- Always validate the signature before trusting any claims
- Always check the exp claim to reject expired tokens
- Use HTTPS for all token transmission
- Use strong signing keys — For HMAC, use at least 256-bit random secrets. For RSA, use 2048-bit or larger keys. Never use weak or predictable secrets.
- Set short expiration times — Access tokens should expire in 15-30 minutes. Use refresh tokens for longer sessions.
- Validate all claims — Check iss (issuer), aud (audience), exp (expiration), and nbf (not before) on every request.
- Use HttpOnly cookies — Store tokens in HttpOnly, Secure, SameSite cookies to prevent XSS attacks from accessing them.
- Implement token rotation — Issue new refresh tokens with each use and invalidate the old ones to detect token theft.
- Maintain a token blocklist — For critical operations (logout, password change), maintain a blocklist of revoked tokens until they expire.
- Avoid the alg:none vulnerability — Always specify allowed algorithms when verifying tokens. Never accept tokens with alg set to "none".
- Keep payloads small — JWTs are sent with every request. Large payloads increase bandwidth usage and latency.
JWT vs. Session-Based Authentication
| Aspect | JWT | Sessions |
|---|---|---|
| Storage | Client-side (stateless) | Server-side (stateful) |
| Scalability | Excellent (no shared state) | Requires shared session store |
| Revocation | Difficult (needs blocklist) | Easy (delete session) |
| Size | Larger (contains claims) | Small (just session ID) |
| Cross-domain | Easy (Authorization header) | Difficult (cookie restrictions) |
| Mobile-friendly | Yes (header-based) | Limited (cookie handling) |
Decode Your JWT Tokens
Use our free JWT decoder to inspect token headers, payloads, and check expiration status. No secret key needed — 100% client-side.
Open JWT Decoder →