DevFlow logoDevFlow
Security
~6 min read
All Glossary Terms

JSON Web Token (JWT)

A JSON Web Token (JWT) is a compact, URL-safe open standard (RFC 7519) used to securely transmit verifiable claims between distributed web services.

Also known as:JWTJSON Web TokenRFC 7519JWSJWEBearer TokenAuth Token

A JSON Web Token (JWT) is an open, industry-standard specification (RFC 7519) that defines a compact, URL-safe mechanism for transmitting digitally verifiable claims between client applications and backend servers using structured JSON objects. JWTs are stateless and self-contained, encapsulating user identity, permissions, and expiration dates directly inside the token payload, eliminating the need for database session lookups on every request.

You can inspect, decode, and debug any token payload in real-time with our client-side JWT Decoder tool.


Anatomy of a JWT: Three Concatenated Segments

A JWT appears as three distinct Base64URL-encoded strings separated by literal periods (.):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiYWRtaW4iOnRydWUsImV4cCI6MTcxNTI0MTYwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
└───────────────────┬──────────────────┘ └─────────────────────────┬────────────────────────┘ └──────────────────────────┬──────────────────────────┘
             1. Header                                      2. Payload                                              3. Signature

1. Header (Metadata)

Identifies the cryptographic algorithm (alg) and token type (typ):

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

2. Payload (Claims)

Contains the assertions about the entity (user) and auxiliary session data. Claims are categorized into:

  • Registered Claims: Predefined, standardized keys recommended by RFC 7519.
  • Public Claims: Custom collision-resistant claims defined in public registries or URIs.
  • Private Claims: Custom agreed-upon keys shared between producer and consumer systems.

3. Signature (Tamper Resistance)

The signature is generated by hashing the Base64URL-encoded header and payload with a secret key or private key:

Signature = HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secretKey
)

Standard Registered Claims (RFC 7519)

Claim Key Full Name Type Purpose & Verification Rule
sub Subject String Unique identifier for the authenticated user or service principal.
iss Issuer String Identifies the identity provider or auth server that minted the token.
aud Audience String/Array Target recipient or API resource servers allowed to accept the token.
exp Expiration Time NumericDate Unix timestamp after which the token must be rejected.
nbf Not Before NumericDate Unix timestamp before which the token must not be accepted.
iat Issued At NumericDate Unix timestamp recording when the auth server issued the token.
jti JWT ID String Unique nonce for the token; used to prevent replay attacks and facilitate blacklisting.

Signing Algorithms: HS256 vs RS256 vs ES256

Feature HS256 (HMAC-SHA256) RS256 (RSA Signature) ES256 (ECDSA P-256)
Cryptography Type Symmetric (Shared Secret) Asymmetric (Public/Private Key) Asymmetric (Elliptic Curve)
Signing Key Shared Secret Private RSA Key (2048+ bits) Private Elliptic Curve Key
Verification Key Same Shared Secret Public RSA Key (JWKS) Public EC Key (JWKS)
Performance Ultra-Fast Moderate Fast
Signature Size 32 bytes (Compact) 256–512 bytes (Large) 64 bytes (Compact)
Optimal Architecture Single monolithic backend Distributed Microservices / Auth0 / Okta High-scale mobile & IoT APIs

Critical Security Vulnerabilities & Developer Mitigations

1. The alg: none Vulnerability

Early flawed JWT libraries allowed malicious clients to alter the header to {"alg": "none"} and strip the signature entirely. If unpatched, backend servers accepted the forged token as valid.

  • Mitigation: Strictly whitelist allowed algorithms on the server (e.g., algorithms: ['RS256']) and reject any token claiming none.

2. Algorithm Confusion Attacks

In systems utilizing asymmetric RS256 keys, attackers take the server's public key (often published openly via .well-known/jwks.json), construct a rogue token, set alg: "HS256", and sign it using the public key as the HMAC secret! If the backend verification routine blindly trusts the header algorithm, it verifies the token using its public key as a symmetric HMAC key.

  • Mitigation: Hardcode the expected algorithm during verification; never dynamically derive the algorithm from the untrusted token header.

3. Token Storage: LocalStorage vs HttpOnly Cookies

Storage Location XSS Protection CSRF Protection Recommendation
localStorage / sessionStorage Vulnerable (Any injected script can steal tokens) Immune Avoid for sensitive production auth.
HttpOnly Cookie (SameSite=Strict, Secure) Protected (JavaScript cannot access cookie) Protected via SameSite Recommended standard for web apps.
In-Memory Variable Protected against persistence Immune Best for short-lived SPA access tokens paired with refresh cookies.

Code Example: Verifying a JWT with Node.js Crypto

import crypto from 'node:crypto';

export function verifyHs256Jwt(token, secret) {
  const parts = token.split('.');
  if (parts.length !== 3) throw new Error('Invalid token structure');

  const [headerB64, payloadB64, signatureB64] = parts;

  // 1. Recompute expected signature using HMAC-SHA256
  const data = `${headerB64}.${payloadB64}`;
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(data)
    .digest('base64url');

  // 2. Timing-safe comparison to prevent timing attacks
  const isValidSig = crypto.timingSafeEqual(
    Buffer.from(signatureB64),
    Buffer.from(expectedSig)
  );

  if (!isValidSig) throw new Error('Tampered signature');

  // 3. Parse and validate claims
  const payload = JSON.parse(Buffer.from(payloadB64, 'base64url').toString('utf8'));
  const now = Math.floor(Date.now() / 1000);

  if (payload.exp && now >= payload.exp) {
    throw new Error('Token has expired');
  }

  return payload;
}

Frequently Asked Questions

Are JWT tokens encrypted?

No. Standard JWTs (specifically JWS - JSON Web Signature) are signed, not encrypted. The header and payload are simply Base64URL-encoded text that anyone can read with our JWT Decoder. Never put passwords, API keys, or raw personal data inside a standard JWT. If confidentiality is required, you must use JWE (JSON Web Encryption).

How do you invalidate or revoke a JWT before it expires?

Because JWTs are stateless, immediate revocation requires maintaining a revocation list:

  1. Short Expiration Windows: Keep Access Tokens short-lived (e.g., 5–15 minutes) and issue new ones via Refresh Tokens.
  2. Token Blacklist / Allowlist in Redis: Store revoked jti (JWT IDs) in an in-memory Redis cache with a TTL equal to the token's remaining lifespan.
  3. User Password / Version Epoch: Include a tokenVersion claim in the payload. When a user logs out of all devices or changes passwords, increment their version in the database to instantly invalidate older tokens.

What is the difference between Access Tokens and Refresh Tokens?

An Access Token is a short-lived credential passed in the HTTP Authorization: Bearer <token> header to access protected API resources. A Refresh Token is a long-lived credential stored securely in an HttpOnly cookie, used exclusively with the auth server to obtain new access tokens without requiring re-authentication.

Interactive Tools for JSON Web Token (JWT)

Free, browser-based utilities to test, generate, and inspect JSON Web Token (JWT) payloads directly.

100% Client-Side • No Telemetry