Skip to content
What is JWT? How JSON Web Tokens Work Explained Simply

What is JWT? How JSON Web Tokens Work Explained Simply

What Is a JWT?

A JSON Web Token (JWT, pronounced “jot”) is a compact, self-contained way to securely transmit information between two parties as a JSON object. JWTs are most commonly used for authentication — after a user logs in, the server creates a JWT and sends it to the client. The client includes the JWT in subsequent requests to prove its identity.

JWTs are everywhere in modern web development. If you have built or used an API with bearer token authentication, you have almost certainly worked with JWTs.

The Structure of a JWT

A JWT consists of three parts separated by dots:

xxxxx.yyyyy.zzzzz
Header.Payload.Signature

Here is a real (decoded) example:

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

The header specifies the signing algorithm (HS256 = HMAC with SHA-256) and the token type.

Payload

{
  "sub": "1234567890",
  "name": "Jane Smith",
  "email": "[email protected]",
  "role": "admin",
  "iat": 1709251200,
  "exp": 1709337600
}

The payload contains claims — statements about the user and metadata. Common claims include:

  • sub (subject): The user identifier
  • iat (issued at): When the token was created
  • exp (expiration): When the token expires
  • iss (issuer): Who created the token
  • aud (audience): Who the token is intended for
  • Custom claims: Any application-specific data (role, permissions, name)

Signature

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

The signature is created by hashing the encoded header and payload with a secret key. This is what makes the JWT tamper-proof — if anyone modifies the header or payload, the signature will not match, and the server will reject the token.

The Complete Token

Each part is Base64Url-encoded and concatenated with dots:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgU21pdGgiLCJpYXQiOjE3MDkyNTEyMDB9.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

That long string of characters is a complete JWT that any server with the secret key can verify and decode.

How JWT Authentication Works

The Login Flow

  1. User submits credentials: Username and password sent to the server.
  2. Server verifies credentials: Checks against the database.
  3. Server creates a JWT: Encodes user information and signs it with a secret key.
  4. Server sends the JWT: Returns the token to the client.
  5. Client stores the JWT: Typically in memory, localStorage, or an HTTP-only cookie.

Subsequent Requests

  1. Client sends the JWT: Included in the Authorization header:
    Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
  2. Server verifies the JWT: Checks the signature and expiration.
  3. Server reads the claims: Extracts user ID, role, and permissions from the payload.
  4. Server processes the request: No database lookup needed to identify the user.

Why This Is Powerful

With traditional session-based auth, the server must look up the session in a database for every request. With JWT, the server can verify and read the token using only the secret key — no database query needed. This makes JWTs ideal for:

  • Stateless APIs: No server-side session storage required
  • Microservices: Any service with the secret key can verify the token
  • Scalability: Load-balanced servers do not need shared session storage

JWT vs Session-Based Auth

FeatureJWTSessions
Server storageNone (stateless)Session store required
ScalabilityExcellent (any server can verify)Requires shared session store
RevocationDifficultEasy (delete session)
SizeLarger (token carries data)Small (just a session ID)
Cross-domainEasyRequires CORS configuration
Mobile friendlyYes (simple header)Cookies can be tricky

Security Best Practices

Use HTTPS Always

JWTs are signed, not encrypted. Anyone who intercepts a JWT can read the payload (it is just Base64-encoded JSON). Always transmit JWTs over HTTPS to prevent interception.

Keep Payloads Small

Do not store sensitive data in the JWT payload. No passwords, credit card numbers, or secrets. The payload is readable by anyone with the token. Include only the minimum claims needed: user ID, role, and expiration.

Set Short Expiration Times

Short-lived tokens (15-60 minutes) limit the damage if a token is compromised. Use refresh tokens to get new access tokens without requiring the user to log in again.

Use Strong Secrets

For HMAC algorithms (HS256, HS384, HS512), use a cryptographically random secret at least 256 bits long. Generate one with the Password Generator — a 32+ character random string works well.

Prefer RS256 Over HS256 for Public APIs

  • HS256 (symmetric): Same secret signs and verifies. Good for single-server applications.
  • RS256 (asymmetric): Private key signs, public key verifies. Better for microservices and public APIs where you want services to verify tokens without having the signing key.

Store Tokens Securely

  • Best: HTTP-only, secure, SameSite cookies (immune to XSS)
  • Acceptable: In-memory (lost on page refresh, but safe from XSS)
  • Risky: localStorage (accessible to any JavaScript on the page, vulnerable to XSS)

Validate Everything

On every request, the server should:

  1. Verify the signature
  2. Check that the token is not expired (exp claim)
  3. Verify the issuer (iss) and audience (aud) if present
  4. Validate that the algorithm matches what you expect (prevents algorithm confusion attacks)

Never Use alg: none

The JWT spec includes an “none” algorithm that produces unsigned tokens. Legitimate use cases are rare. Always reject tokens with alg: none unless you have a specific reason not to.

Common JWT Pitfalls

Not Handling Token Expiration

If your client does not check expiration and refresh tokens proactively, users get logged out with failed requests instead of smooth re-authentication.

Storing Too Much Data

Every claim increases the token size, and the token is sent with every request. A JWT with 20 custom claims bloats every API call. Keep it minimal.

No Revocation Strategy

JWTs cannot be invalidated by default (they are stateless). If you need to revoke access (user banned, password changed, suspicious activity), you need a strategy:

  • Short expiration + refresh tokens: Tokens expire quickly; refuse to refresh revoked users
  • Token blacklist: Maintain a list of revoked tokens (partially defeats the stateless advantage)
  • Token versioning: Include a version number in the JWT; increment it on revocation

Algorithm Confusion Attacks

Some JWT libraries have been vulnerable to attacks where the attacker changes the algorithm in the header (e.g., from RS256 to HS256) and signs with the public key. Always explicitly specify the expected algorithm when verifying tokens.

Decoding JWTs

JWTs are not encrypted — they are signed. You can decode any JWT to see its contents:

// Decode the payload (no verification)
const payload = JSON.parse(atob(token.split('.')[1]));
console.log(payload);

This is useful for debugging, but remember: decoding is not verification. Always verify the signature on the server before trusting any claims.

Conclusion

JWT is a powerful, widely-adopted standard for API authentication. It enables stateless, scalable systems where any server can verify user identity without shared session storage. The key is using JWTs correctly: short expiration times, strong secrets, HTTPS-only transmission, and minimal payloads.

For generating strong secrets for JWT signing, use the Password Generator. To hash data for integrity checks alongside your JWT workflow, try the Hash Generator.