All articles

Understanding JWT Tokens: How to Decode and Inspect Them

6 min read
JWTauthenticationsecurity

A JSON Web Token (JWT) is a compact, URL-safe string used to pass claims between parties — typically between a client and a server. You will encounter them constantly in authentication flows: after logging in, your server issues a JWT; your client sends it back on every subsequent request; the server verifies it without touching a database.

Understanding what is inside a JWT — and what is not — helps you use them correctly and avoid common security mistakes.

The three parts

A JWT looks like this: xxxxx.yyyyy.zzzzz — three Base64URL-encoded segments separated by dots.

Header

The first segment is the header. Decoded, it is a small JSON object that declares the token type and the signing algorithm:

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

alg tells the recipient how the token was signed. HS256 is HMAC with SHA-256 (a symmetric algorithm — both sides share the same secret). RS256 is RSA with SHA-256 (asymmetric — the server signs with a private key; clients verify with a public key).

Payload

The second segment is the payload — the claims. Claims are statements about a subject (usually a user). There are three kinds:

  • Registered claims — standardised, short names: sub (subject, typically a user ID), iss (issuer), exp (expiration time as a Unix timestamp), iat (issued at), aud (audience).
  • Public claims — names registered in the IANA JWT Claims Registry to avoid collisions. Examples: email, name, picture.
  • Private claims — application-specific data agreed upon by both parties. Examples: role, tenantId, permissions.

Signature

The third segment is the signature, which is computed by taking the encoded header, a dot, the encoded payload, and signing the whole thing with the algorithm declared in the header. For HS256:

HMAC-SHA256(base64url(header) + "." + base64url(payload), secret)

The signature lets the server verify that the token has not been tampered with. If anyone modifies the payload — to change their role from user to admin, for example — the signature will not match, and the server will reject the token.

The critical security point: JWTs are not encrypted

A standard JWT (JWS — JSON Web Signature) is signed, not encrypted. The header and payload are Base64URL encoded, which is reversible by anyone — no key needed. Anyone who can see the token can read every claim inside it.

This means: never put sensitive information like passwords, credit card numbers, or PII in a JWT payload unless you are using JWE (JSON Web Encryption) instead. The signature only guarantees the token has not been modified — it does not hide the contents.

Decoding a JWT without any tools

You can decode a JWT in any terminal with:

echo "eyJ..." | cut -d'.' -f2 | base64 -d

Replace the second -f2 with -f1 for the header. Note that Base64URL encoding uses - and _ instead of + and /, and padding is omitted — some tools require you to re-add padding before decoding.

Expiry and refresh tokens

The exp claim is a Unix timestamp. Once that timestamp passes, a correctly implemented server will reject the token. Short-lived access tokens (5–60 minutes) are paired with longer-lived refresh tokens (days to weeks). When the access token expires, the client uses the refresh token to get a new one — without asking the user to log in again.

When you decode a JWT and see an exp value, you can compare it to the current Unix time (Date.now() / 1000 in JavaScript) to see how long the token is valid for, or whether it has already expired.

What a JWT decoder tool is good for

A JWT decoder is most useful when debugging authentication issues: verifying that the claims your server is issuing match what your application expects, checking that expiry times are set correctly, or confirming that a user has the roles you think they do. Since the payload is only Base64-encoded, decoding requires no key. Verifying the signature, however, requires the secret or public key — a decoder tool can show you what is in the token, but cannot confirm the signature is valid without that key.