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.

Advertisement

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_adQssw5c

1. 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):

ClaimNameDescription
issIssuerWho issued the token
subSubjectWho the token is about (usually user ID)
audAudienceIntended recipient of the token
expExpirationWhen the token expires (Unix timestamp)
nbfNot BeforeToken is not valid before this time
iatIssued AtWhen the token was created
jtiJWT IDUnique identifier for the token

How JWT Authentication Works

Here is the typical JWT authentication flow:

1

User Login

User sends credentials (email/password) to the authentication server.

2

Server Validates

Server verifies credentials against the database. If valid, creates a JWT with user claims.

3

Token Returned

Server sends the JWT back to the client (in response body or Set-Cookie header).

4

Client Stores Token

Client stores the JWT (in HttpOnly cookie or memory — avoid localStorage for sensitive tokens).

5

Authenticated Requests

Client includes the JWT in the Authorization header: Bearer <token> for subsequent API requests.

6

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

AspectJWTSessions
StorageClient-side (stateless)Server-side (stateful)
ScalabilityExcellent (no shared state)Requires shared session store
RevocationDifficult (needs blocklist)Easy (delete session)
SizeLarger (contains claims)Small (just session ID)
Cross-domainEasy (Authorization header)Difficult (cookie restrictions)
Mobile-friendlyYes (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 →
Advertisement