DEV Community

Snappy Tools
Snappy Tools

Posted on

What's Actually Inside a JWT Token (and How to Read One)

If you've worked with any modern authentication system — OAuth, Auth0, Firebase, Supabase, your own API — you've dealt with JWT tokens. You've probably copied one from a network request and stared at a long string of letters that looks like random noise:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTc0NTQ4ODgwMCwiZXhwIjoxNzQ1NDkyNDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Enter fullscreen mode Exit fullscreen mode

It's not random. Every part means something specific. Here's exactly what's in there.

The structure: three parts separated by dots

A JWT is always three Base64URL-encoded sections joined by dots:

[header].[payload].[signature]
Enter fullscreen mode Exit fullscreen mode

If you split the token above on ., you get three separate strings. Decode each one (Base64URL → JSON) and the structure becomes clear.

Part 1: The header

The first section encodes a small JSON object that describes the token itself:

{
  "alg": "HS256",
  "typ": "JWT"
}
Enter fullscreen mode Exit fullscreen mode
  • alg — the algorithm used to sign the token. Common values: HS256 (HMAC-SHA256), RS256 (RSA + SHA256), ES256 (ECDSA + SHA256)
  • typ — always "JWT" for JWT tokens

The header tells the receiving server how to verify the signature. Different algorithms have different security properties — HS256 uses a shared secret, RS256 uses public/private key pairs.

Part 2: The payload (the interesting part)

The payload is where the actual data lives. It's another Base64URL-encoded JSON object:

{
  "sub": "user_123",
  "name": "John Doe",
  "iat": 1745488800,
  "exp": 1745492400
}
Enter fullscreen mode Exit fullscreen mode

These key-value pairs are called claims. Some are standardised (defined in RFC 7519):

Claim Full name Meaning
sub Subject Who the token is about (usually a user ID)
iss Issuer Who created the token (your auth server's domain)
aud Audience Who the token is intended for
exp Expiration time Unix timestamp — token invalid after this
nbf Not before Unix timestamp — token invalid before this
iat Issued at Unix timestamp — when the token was created
jti JWT ID Unique identifier for this token (prevents replay)

Beyond these, you can include any custom claims — roles, permissions, user metadata. Whatever your application needs.

Part 3: The signature

The signature is created by:

  1. Taking base64url(header) + "." + base64url(payload)
  2. Signing that string using the algorithm and key specified in the header

For HS256:

HMAC-SHA256(
  secret_key,
  base64url(header) + "." + base64url(payload)
)
Enter fullscreen mode Exit fullscreen mode

The server that receives the token re-runs this calculation and compares the result to the signature in the token. If they match, the payload hasn't been tampered with.

Important: the signature verifies integrity, not confidentiality. The payload is Base64URL-encoded, not encrypted. Anyone can read the claims in a JWT — just paste it into a decoder. Never put sensitive data (passwords, full credit card numbers, SSNs) in a JWT payload.

Decoding vs verifying

There's a difference developers often blur:

Decoding means base64url-decoding the header and payload to read the JSON. You don't need the secret key for this. It tells you what's claimed — but you can't trust it without verification.

Verifying means checking the signature using the correct key. This confirms the token was issued by who it claims and hasn't been modified.

When debugging, you just want to decode — see what claims the server put in the token, check the expiry, inspect the user ID. You don't need the secret key for that.

Try it: paste any JWT into the SnappyTools Base64 Encoder/Decoder — just take the middle section (the payload), add padding if needed (= chars), and decode it. You'll see the raw JSON. Or use the JSON Formatter to prettify the output.

Common mistakes

Mistake 1: putting secrets in the payload

The payload is readable by anyone who has the token. If you put "creditCard": "4111..." in there, anyone who intercepts or steals the token can read it. Use JWEs (JSON Web Encryption) if you need confidentiality, or store sensitive data server-side and only put a reference ID in the JWT.

Mistake 2: ignoring expiry

The exp claim is just a number. The server receiving the token is responsible for checking it. If your code doesn't validate exp, expired tokens keep working forever. Always validate the expiry.

Mistake 3: algorithm confusion

The alg field in the header tells the server which algorithm to use for verification. Early JWT libraries trusted the header unconditionally — an attacker could set alg: none and submit an unsigned token that passed verification. Modern libraries fix this, but always configure your library to accept only specific algorithms, not whatever the token claims.

Mistake 4: storing JWTs in localStorage

Tokens in localStorage are accessible to JavaScript, including malicious scripts injected via XSS attacks. For long-lived tokens, prefer httpOnly cookies — inaccessible to JavaScript. Short-lived access tokens in memory (not persisted) are a reasonable tradeoff for some SPAs.

When the token expires

The exp claim is a Unix timestamp. If exp: 1745492400, the token is valid until that exact second. Many systems issue short-lived access tokens (15 minutes to 1 hour) paired with longer-lived refresh tokens — the access token expires frequently, the refresh token is used to get a new one without re-authentication.

To debug expiry issues: decode the token, find the exp claim, and convert the Unix timestamp to a readable date. The difference between iat and exp tells you the token's intended lifetime.


Understanding JWT structure makes debugging authentication much less frustrating. When something breaks — invalid token, expired token, wrong permissions — you can decode the token directly and see exactly what claims are in there, rather than guessing. The three-part structure is simple once you've seen it a few times, and the signing mechanism is one of the more elegant solutions to stateless authentication.

Top comments (0)