JWT Decoded: The 3 Parts Every Developer Should Understand

1 9 115
calendar_todayschedule3 min read

If you've ever built an API that authenticates users, you've probably encountered a JWT. You know it starts with eyJ, it has two dots in it, and when something goes wrong, your auth middleware throws an error that tells you nothing useful.

Here's what's actually inside one — and why understanding the structure saves time when debugging.

What a JWT looks like

A JSON Web Token is three Base64URL-encoded strings, joined by dots:

``

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

`

Split it at the dots and you get:

  1. Header — algorithm and token type
  2. Payload — the actual claims (user data)
  3. Signature — the verification proof

Each part is Base64URL encoded, not encrypted. This is a key point many developers miss.

Part 1: The Header

Decode the first segment:

<code>json <p>{</p> <p>"alg": "HS256",</p> <p>"typ": "JWT"</p> <p>}</p> </code>

alg tells the consumer which algorithm was used to sign the token. Common values:
HS256 — HMAC-SHA256 (shared secret, symmetric)RS256 — RSA with SHA-256 (public/private key pair, asymmetric)ES256 — ECDSA with SHA-256 (elliptic curve, asymmetric)
Security note: Some older libraries accepted
alg: none, which means no signature verification. Never trust a library that does this without explicit configuration.

Part 2: The Payload

This is where your actual data lives. Decode the second segment:

<code>json <p>{</p> <p>"sub": "1234567890",</p> <p>"name": "Jane Doe",</p> <p>"iat": 1516239022,</p> <p>"exp": 1516325422,</p> <p>"iss": "auth.example.com",</p> <p>"aud": "api.example.com"</p> <p>}</p> </code>

The spec defines a set of registered claims — standard field names every JWT library understands:

| Claim | Name | Description |

|-------|------|-------------|

| sub | Subject | Who the token is about (usually a user ID) |

| iss | Issuer | Who issued the token |

| aud | Audience | Who should accept the token |

| exp | Expiration | Unix timestamp — reject after this time |

| iat | Issued At | Unix timestamp — when it was created |

| nbf | Not Before | Unix timestamp — don't accept before this time |

| jti | JWT ID | Unique ID for this token (prevents replay attacks) |

Anything else in the payload is a private claim — custom data your application defined. Common ones: role, email, permissions.

Critical: the payload is not encrypted. It is only Base64URL encoded. Anyone who has the token can read the payload. Do not put sensitive data (passwords, full card numbers, PII you want hidden) in a JWT payload unless you're using JWE (JSON Web Encryption) instead.

Part 3: The Signature

The signature is what prevents tampering. It is computed as:

<code> <p>HMACSHA256(</p> <p>base64UrlEncode(header) + "." + base64UrlEncode(payload),</p> <p>secret</p> <p>)</p> </code>

If anyone modifies a single character in the header or payload, the signature check fails. This is the only part that requires your secret key to verify. The header and payload can be decoded by anyone.

Debugging JWTs in practice

The fastest way to debug a JWT is to decode it and look at the payload directly. The most common issues:

Token is expired — check exp. Convert the Unix timestamp: a value of 1716239022 is roughly May 2024. If exp is in the past, the token is invalid. Re-issue it.

Wrong audience — if your API rejects tokens from another service, check aud. Many auth flows issue tokens with a specific audience claim, and APIs are supposed to reject tokens meant for someone else.

Wrong issuer — check iss. If your API only accepts tokens from auth.yourapp.com and it gets a token issued by dev.yourapp.com, it should reject it.

Claims are missing — if your middleware expects a role claim and it isn't there, it's a configuration issue on the auth server, not in your API.

You don't need a key to inspect the header and payload — just Base64URL decode each segment. You only need the key to verify the signature.

Quick decode in JavaScript

`javascript
function decodeJWT(token) {


const parts = token.split('.');


if (parts.length !== 3) throw new Error('Invalid JWT');

const decode = (str) => {

const base64 = str.replace(/-/g, '+').replace(/_/g, '/');

return JSON.parse(atob(base64));

};

return {

header: decode(parts[0]),

payload: decode(parts[1]),

};

}

`

For verification, use a proper library like jsonwebtoken (Node.js) or python-jose (Python) — don't roll your own signature verification.

A faster way to inspect tokens

If you need to quickly inspect a JWT while debugging — check expiry, confirm claims, verify the algorithm — use a browser-based decoder. The one at snappytools.app/jwt-decoder shows decoded header and payload, flags expired tokens, labels standard claims with human-readable names, and never sends your token to a server.

Pasting tokens into random online tools is a security risk in production environments. A client-side decoder is the right default — your token stays in your browser.

Summary

  • A JWT has three parts: header, payload, signature — separated by dots
  • Header: algorithm and type
  • Payload: claims — standard (sub, exp, iss, aud`) and custom
  • Signature: prevents tampering, requires your secret to verify
  • The payload is encoded, not encrypted — anyone can read it
  • For debugging: decode the header and payload to find expiry, audience, and issuer issues
  • For verification: always use a battle-tested library, never roll your own

Originally published at https://snappytools.app/jwt-decoder/

🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

Comparison: Universal Import vs. Plaid/Yodlee

Pocket Portfolio - Mar 12

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelski - Apr 23

Sovereign Intelligence: The Complete 25,000 Word Blueprint (Download)

Pocket Portfolio - Apr 1

The 3-Row Snapshot: Privacy-Preserving Inference

Pocket Portfolio - Feb 26

The Interface of Uncertainty: Designing Human-in-the-Loop

Pocket Portfolio - Mar 10
chevron_left
2.4k Points125 Badges
101Posts
0Comments
SnappyTools builds free, fast, browser-based tools for developers, writers, and designers. No signup... Show more

Related Jobs

Commenters (This Week)

6 comments
2 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!