JSON Web Tokens (JWTs) are everywhere in modern web auth. If you’ve ever worked with authentication, you’ve probably encountered one. Here’s how to decode a JWT and read its claims — and why verification matters.

What Is a JWT?

A JWT is a compact, URL-safe way to represent claims between two parties. It’s commonly used for stateless authentication in SPAs and APIs.

A JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjgzOTAyMiwiZXhwIjoxNzM4NDE1MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

It has three parts separated by dots: Header, Payload, Signature.

The Three Parts of a JWT

1. Header

Base64URL-encoded JSON containing metadata:

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

HS256 means the signature was created using HMAC-SHA256.

2. Payload

Base64URL-encoded JSON containing the claims — statements about the user:

{
  "sub": "1234567890",
  "name": "Jane Doe",
  "admin": true,
  "iat": 1516839022,
  "exp": 1734415022
}

Key claims:

  • sub — subject (usually the user ID)
  • iat — issued at (Unix timestamp)
  • exp — expiration time (Unix timestamp)
  • admin — custom claim example

3. Signature

The header and payload are signed with a secret key. This proves the token wasn’t tampered with. Without verification, anyone can modify the payload and re-encode it.

How to Decode a JWT

You can decode the header and payload (not the signature) without a key:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
        ↓ Base64URL decode
{"alg":"HS256","typ":"JWT"}
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjgzOTAyMiwiZXhwIjoxNzM4NDE1MDIyfQ
        ↓ Base64URL decode
{"sub":"1234567890","name":"Jane Doe","admin":true,"iat":1516839022,"exp":1734415022}

Important: Decoding only reads the claims. It does NOT verify the signature. Only the signature step confirms the token hasn’t been tampered with.

How to Check If a JWT Is Expired

The exp claim contains a Unix timestamp of when the token expires. Compare it to the current time:

const exp = 1734415022;        // from token payload
const now = Math.floor(Date.now() / 1000);

if (exp < now) {
  console.log("Token is expired");
} else {
  console.log("Token is still valid");
}

Unix timestamps are seconds since January 1, 1970. The above example token expires on December 17, 2024.

Why Signature Verification Matters

You should never trust a JWT based solely on decoded claims. Without verifying the signature:

  1. An attacker can modify the payload to set admin: true
  2. They can change sub to any user ID
  3. They can extend or remove the exp claim
  4. They re-encode header + payload and forge a token

A server must verify the signature using the secret key (or public key for asymmetric algorithms) before trusting any claim.

Common JWT Algorithms

| Algorithm | Type | Use | |-----------|------|-----| | HS256 | Symmetric | Shared secret key | | HS384 | Symmetric | Shared secret key | | HS512 | Symmetric | Shared secret key | | RS256 | Asymmetric | Public/private key pair | | RS384 | Asymmetric | Public/private key pair | | RS512 | Asymmetric | Public/private key pair | | ES256 | Asymmetric | ECDSA with P-256 curve |

Summary

A JWT has three parts: header, payload, and signature. The header and payload are Base64URL-encoded JSON anyone can decode. The signature must be verified with a key to prove authenticity. Always check exp to see if a token has expired.

Decode any JWT instantly with the JWT Decoder — paste your token and read all claims right in the browser.