
How JWT Works — Complete Authentication Developer Guide (2026)
Step-by-step deep explanation of JWT creation, header + payload + signature structure, secure storage, transmission, backend validation, refresh tokens and production security best practices.

Santosh Gautam
Full Stack Developer · India
1. What is JWT?
JWT (JSON Web Token) is a digitally signed, stateless authentication token used to securely transmit information between client and server. It is the industry standard for token-based authentication in APIs, SPAs and mobile apps.
Header.Payload.Signature eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJpZCI6MTAxLCJlbWFpbCI6InVzZXJAZ21haWwuY29tIiwicm9sZSI6ImFkbWluIn0. XyZaBcDeFgHiJkLmNoPqRsTuVwXyZ123456
- JWT is stateless — server does not store session.
- Contains encoded user identity information.
- Digitally signed using a secret key (HMAC-SHA256).
- Can include expiration time via
expclaim. - Widely used in Vue.js, React SPAs and REST APIs.
JWT is NOT encrypted by default — it is only signed. Anyone can decode the payload, but cannot modify it without the secret key.
2. How JWT is Created (Backend — Node.js)
const express = require("express");
const jwt = require("jsonwebtoken");
const app = express();
app.use(express.json());
app.post("/login", (req, res) => {
const { email, password } = req.body;
// Validate credentials
if (email !== "user@gmail.com" || password !== "123456") {
return res.status(401).json({ message: "Invalid credentials" });
}
const payload = {
id: 101,
email: email,
role: "admin"
};
const token = jwt.sign(
payload,
process.env.JWT_SECRET,
{ expiresIn: "1h" }
);
res.json({ token });
});- Login route receives credentials from client.
- Server validates credentials against database.
- Payload contains user identity data (no sensitive info).
jwt.sign()creates digitally signed token.expiresInensures automatic expiration.- Token is sent back to frontend in response.
3. Internal JWT Structure — Header, Payload, Signature
// HEADER — Algorithm & token type
{
"alg": "HS256",
"typ": "JWT"
}
// PAYLOAD — User claims (base64 encoded)
{
"id": 101,
"email": "user@gmail.com",
"role": "admin",
"exp": 1712345678
}
// SIGNATURE — Integrity check
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secretKey
)- Header — defines algorithm used for signing.
- Payload — stores user data and claims.
- Signature — ensures token integrity and authenticity.
- If payload is tampered, signature becomes invalid.
- Base64 URL encoding makes token transmission safe.
Never store passwords, credit card numbers or sensitive PII in the JWT payload — it is only base64 encoded, not encrypted.
4. Storing JWT in Frontend
// ✅ Recommended — HttpOnly Cookie (most secure)
// Set by server, not accessible via JavaScript
// ⚠️ Common but risky — localStorage
axios.post("/login", { email, password })
.then(response => {
localStorage.setItem("token", response.data.token);
});- Token is stored after successful login response.
localStorage— easy but vulnerable to XSS attacks.httpOnly cookie— more secure, not accessible via JS.- Token must be cleared on logout.
- Never store sensitive data in payload — it is readable by anyone.
Production recommendation: Use HttpOnly + Secure + SameSite=Strict cookies instead of localStorage for JWT storage.
5. Sending JWT in Authorization Header
// Include JWT in every protected API request
axios.get("/dashboard", {
headers: {
Authorization: "Bearer " + localStorage.getItem("token")
}
});- Authorization header carries JWT with every request.
Beareris the standard authentication scheme.- Backend middleware extracts token from header.
- Every protected route requires a valid token.
- Without valid token → 401 Unauthorized response.
6. Backend Token Validation Middleware
function verifyToken(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ message: "No token provided" });
}
const token = authHeader.split(" ")[1]; // Extract after "Bearer "
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
return res.status(401).json({ message: "Invalid or expired token" });
}
req.user = decoded; // Attach user data to request
next();
});
}
// Protected route
app.get("/dashboard", verifyToken, (req, res) => {
res.json({ message: "Welcome " + req.user.email });
});- Server reads Authorization header from request.
- Token is extracted after "Bearer " prefix.
jwt.verify()checks signature validity using secret key.- If invalid or expired → request rejected with 401.
- If valid → decoded payload attached to
req.user. - Route access granted securely without database lookup.
7. JWT Security Best Practices
Always use HTTPS
Encrypt all traffic in production.
Strong Secret Key
Use 256-bit random secret via env vars.
Short Expiration
15 min access token, 7 day refresh.
Refresh Token Rotation
Issue new refresh token on every use.
HttpOnly Cookies
Prevent XSS token theft.
Rate Limit Login
Block brute force attacks on /login.
No Sensitive Payload
Never store passwords or PII in JWT.
Revocation Strategy
Blacklist or short-expiry for logout.
8. JWT vs Session Authentication
| Aspect | JWT | Session |
|---|---|---|
| State | Stateless | Stateful (server stores) |
| Storage | Client-side (cookie/storage) | Server-side (DB / Redis) |
| Scalability | Excellent — no server storage | Requires shared session store |
| Revocation | Complex (blacklist needed) | Easy — delete server record |
| Mobile/SPA | Perfect fit | Cookie complexity on mobile |
| DB Lookups | None per request | Every request hits DB |
9. Production Architecture — Access + Refresh Token Flow
User → Login Request
↓
Backend Validates Credentials
↓
Access Token (15 min expiry) ← Short-lived, used for API calls
Refresh Token (7 days expiry) ← Long-lived, stored securely
↓
Frontend stores tokens
↓
Protected API Request with Access Token
↓
Backend verifies Access Token
↓
If expired → Send Refresh Token to /refresh endpoint
↓
Backend issues New Access Token
↓
Continue session seamlesslyThis dual-token architecture improves both security and user experience in production systems — short-lived access tokens minimize risk while refresh tokens maintain seamless sessions.
10. Common JWT Mistakes Developers Make
- Storing sensitive data (passwords, SSN) inside payload.
- Using weak or hardcoded secret keys in source code.
- Not setting
expiresIn— tokens never expire. - Using HTTP instead of HTTPS in production.
- Not handling token expiration gracefully on frontend.
- Exposing
JWT_SECRETin frontend code or git repository.
The #1 JWT mistake: Storing JWT_SECRET directly in code. Always use environment variables and rotate secrets regularly in production.
11. Frequently Asked Questions (JWT)
JWT is signed, not encrypted by default. The payload is base64 encoded — anyone can decode and read it. Use JWE (JSON Web Encryption) if you need encrypted payloads.