JWT Authentication Explained: How JSON Web Tokens Work and When to Use Them

JWT Authentication Explained: How JSON Web Tokens Work and When to Use Them

by | Jul 7, 2026 | Uncategorized | 0 comments

JWT authentication has become the default way to secure modern APIs, single-page apps, and microservices. But behind its popularity hides a fair amount of confusion: How is a token actually built? Is it really safer than sessions? What happens when it expires? In this guide, we break down everything you need to know about JSON Web Tokens, with real code examples and the pitfalls we see most often in production.

What is JWT Authentication?

A JSON Web Token (JWT) is a compact, URL-safe string that securely represents claims between two parties. In an authentication context, the server issues a JWT after a user logs in successfully. The client then sends this token with every subsequent request, and the server verifies it without needing to look up a session in a database.

This makes JWT authentication stateless, which is why it pairs so well with REST APIs, mobile apps, and distributed systems.

json web token security

The Anatomy of a JWT: Header, Payload, Signature

A JWT looks like three Base64-encoded strings separated by dots:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkphbmUiLCJleHAiOjE3NDk4ODAwMDB9.s5K3Z1Xq...signature

1. Header

Describes the token type and the signing algorithm used.

{
  "alg": "HS256",
  "typ": "JWT"
}

2. Payload

Contains the claims: information about the user and metadata about the token itself.

{
  "sub": "1234",
  "name": "Jane Doe",
  "role": "admin",
  "iat": 1749876400,
  "exp": 1749880000
}

Common standard claims include:

  • sub : subject (user ID)
  • iat : issued at
  • exp : expiration time
  • iss : issuer
  • aud : audience

Important: the payload is encoded, not encrypted. Anyone can decode it. Never put passwords or sensitive secrets in there.

3. Signature

The signature ensures the token has not been tampered with. It is computed like this:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

If the payload is modified, the signature no longer matches, and the server rejects the token.

How the JWT Authentication Flow Works

Here is the typical exchange between a client and a server:

  1. The user submits credentials (email + password) to /login.
  2. The server validates them and generates a signed JWT.
  3. The client stores the token (memory, httpOnly cookie, or secure storage).
  4. For every protected request, the client sends the token in the Authorization: Bearer <token> header.
  5. The server verifies the signature and the expiration, then processes the request.
json web token security

JWT vs Session-Based Authentication

Choosing between JWT and traditional sessions is one of the most common architecture decisions. Here is a clear comparison:

Criteria JWT (Stateless) Sessions (Stateful)
Storage Client-side Server-side store (DB, Redis)
Scalability Excellent across services Requires shared session store
Revocation Hard (needs blocklist) Easy (delete session)
Performance No DB hit per request Lookup on every request
Best for APIs, microservices, mobile Classic web apps

Practical Example: Issuing and Validating a JWT in Node.js

Issuing a token at login

const jwt = require('jsonwebtoken');

app.post('/login', async (req, res) => {
  const user = await authenticate(req.body.email, req.body.password);
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });

  const token = jwt.sign(
    { sub: user.id, role: user.role },
    process.env.JWT_SECRET,
    { expiresIn: '15m', issuer: 'devchatt.org' }
  );

  res.json({ access_token: token });
});

Validating a token in middleware

function authMiddleware(req, res, next) {
  const header = req.headers.authorization;
  if (!header || !header.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Missing token' });
  }

  const token = header.split(' ')[1];

  try {
    const payload = jwt.verify(token, process.env.JWT_SECRET, {
      issuer: 'devchatt.org',
      algorithms: ['HS256']
    });
    req.user = payload;
    next();
  } catch (err) {
    if (err.name === 'TokenExpiredError') {
      return res.status(401).json({ error: 'Token expired' });
    }
    return res.status(401).json({ error: 'Invalid token' });
  }
}

Handling Token Expiration the Right Way

Short-lived tokens are safer, but they create a UX problem: users get kicked out. The standard solution is the access token + refresh token pattern:

  • Access token: short lifespan (5 to 15 minutes), sent on every request.
  • Refresh token: long lifespan (days or weeks), stored securely (httpOnly cookie), used only to get a new access token.

When the access token expires, the client silently calls /refresh to obtain a new one. The refresh token can be revoked server-side, giving you back the control you lose with pure JWT.

json web token security

Common JWT Pitfalls to Avoid

  • Storing tokens in localStorage: vulnerable to XSS. Prefer httpOnly cookies for the refresh token.
  • Accepting the none algorithm: always whitelist algorithms during verification.
  • Putting sensitive data in the payload: it is just Base64, not encrypted.
  • Using long expiration times: a stolen token with a 30-day expiry is a disaster.
  • Forgetting to validate iss and aud: these protect against tokens issued by another system.
  • No revocation strategy: implement a blocklist for critical actions like logout-everywhere.
  • Weak secrets: use at least 256 bits of entropy, or switch to RS256 with key pairs.

When Should You Use JWT Authentication?

Good fit:

  • REST or GraphQL APIs consumed by SPAs or mobile apps
  • Microservices that need to pass identity across boundaries
  • Service-to-service authentication
  • Stateless serverless functions

Probably not the best fit:

  • Traditional server-rendered web apps where sessions work perfectly
  • Apps that need immediate, granular revocation of credentials
  • Small monolithic projects where Redis sessions are simpler

FAQ: JWT Authentication

How does JWT authentication work in simple terms?

The server gives the client a signed token after login. The client sends that token with every request, and the server checks the signature to confirm the user is authenticated, without storing anything server-side.

What is the difference between JWT and OAuth?

OAuth 2.0 is a framework for authorization (how an app gets permission to access resources). JWT is a token format. OAuth often uses JWTs as its access tokens, but they solve different problems.

Is JWT the same as JSON?

No. JWT uses JSON for its header and payload, but the final token is a Base64-encoded, signed string. JSON is just the data format used inside.

Are JWTs still used in 2026?

Yes, heavily. They remain the standard for API authentication, OpenID Connect, and microservice communication. The best practices have just matured around shorter lifespans and refresh tokens.

Where should I store a JWT on the client?

Store short-lived access tokens in memory, and refresh tokens in httpOnly, Secure, SameSite cookies. Avoid localStorage for anything sensitive due to XSS risk.

Can a JWT be revoked before it expires?

Not natively, since the server does not track them. You need a revocation list (often in Redis) or you rely on short expiration plus refresh token revocation.

Final Thoughts

JWT authentication is powerful, but it is not magic. Used correctly, with short-lived tokens, refresh rotation, strong secrets, and proper claim validation, it gives you a scalable and clean authentication layer. Used carelessly, it becomes a security liability. Take the time to design the flow before writing the first jwt.sign(), and your future self will thank you.