Skip to content
Santosh Gautam - Full Stack Developer Gurugram India
SANTOSH GAUTAM Full Stack Developer
Authentication 10 min read · Updated March 2026 Developer Guide
How JWT Works — Complete Authentication Developer Guide 2026 by Santosh Gautam

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

Full Stack Developer · India

JWTAuthenticationNode.jsSecurityAPI

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 exp claim.
  • 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.
  • expiresIn ensures 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.

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

AspectJWTSession
StateStatelessStateful (server stores)
StorageClient-side (cookie/storage)Server-side (DB / Redis)
ScalabilityExcellent — no server storageRequires shared session store
RevocationComplex (blacklist needed)Easy — delete server record
Mobile/SPAPerfect fitCookie complexity on mobile
DB LookupsNone per requestEvery 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 seamlessly

This 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_SECRET in 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.