GameParticles
Developer

How to Decode a JWT and Read Its Claims Safely

Decode a JWT by splitting its three base64url segments, read claims like iss, sub and exp, and learn why decoding a token is not the same as verifying it.

Updated

A JWT is three base64url-encoded segments separated by dots: header, payload and signature. To read the claims, split on ., base64url-decode the first two segments, and parse the resulting JSON. Decoding needs no key, but it also proves nothing; only verifying the signature tells you the token is genuine.

Anatomy of a token

A typical JWT looks like eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkFkYSIsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c. The three parts are:

  • Header: {"alg":"HS256","typ":"JWT"}. Says which algorithm signed the token and optionally a key id (kid).
  • Payload: {"sub":"1234","name":"Ada","iat":1516239022}. The claims, which is the data you actually care about.
  • Signature: bytes produced by signing header.payload with the issuer's key. Not JSON, so it does not decode to anything readable.

Every JWT header starts with eyJ because that is what {" encodes to in Base64. If you see eyJ, you are almost certainly looking at a JWT.

Decoding by hand

The segments use base64url, not standard Base64: - instead of +, _ instead of /, and no = padding. Most decoders accept the URL-safe alphabet but some insist on padding. To decode in a strict tool, replace the characters and append = until the length is a multiple of four. Then decode and you have JSON text.

In JavaScript the quick version is JSON.parse(atob(token.split(".")[1].replace(/-/g,"+").replace(/_/g,"/"))). In Python, base64.urlsafe_b64decode(seg + "=" * (-len(seg) % 4)) handles the padding arithmetic for you. On the command line, cut -d. -f2 | base64 -d works once padding is fixed.

If you just want to see what is inside without writing code, paste the token into the JWT Decoder. It splits the segments, decodes header and payload, explains each standard claim, and shows a countdown to expiry.

To inspect the bytes of a single segment yourself, drop it into the Base64 decoder after fixing the padding; the output is the raw JSON text.

The claims you will meet

Registered claims are short names defined in RFC 7519. The common ones:

  • iss (issuer): who created the token, usually a URL such as https://accounts.example.com.
  • sub (subject): the user or principal the token is about.
  • aud (audience): who the token is intended for. Your API should reject tokens whose aud does not match it.
  • exp (expiration): Unix timestamp in seconds after which the token is invalid.
  • nbf (not before): the token is invalid before this time.
  • iat (issued at): when it was created.
  • jti: unique token id, used to prevent replay.

Everything else is custom: email, roles, scope, tenant_id and so on. Custom claims mean whatever the issuer says they mean, so check your identity provider's documentation rather than guessing.

Timestamps are seconds, not milliseconds. 1516239022 is January 18, 2018. A value like 1516239022000 is a bug on the issuer's side and will be read as a date tens of thousands of years away by any correct validator.

Decoding is not verifying

This is the part people get wrong. Anyone can build a token with {"sub":"admin","roles":["superuser"]} in the payload and any string in the signature slot. It decodes perfectly. It is also worthless unless the signature checks out against the issuer's key.

Verification depends on alg:

  • HS256, HS384, HS512: HMAC with a shared secret. The verifier needs the same secret the issuer used. The signature is an HMAC-SHA digest of header.payload; a hash generator shows what a plain SHA-256 digest looks like, but HMAC also mixes in the key, so verification belongs in a library on your server.
  • RS256, ES256, PS256: asymmetric. The issuer signs with a private key; you verify with the public key, typically fetched from a JWKS endpoint such as /.well-known/jwks.json and matched by kid.
  • none: no signature. Reject these outright.

Use a maintained library (jsonwebtoken or jose in Node, PyJWT in Python, java-jwt on the JVM) and pass it the expected algorithm list, issuer and audience. Never let the token's own alg header decide how it is verified; that is the root of several real-world bypasses.

Decode to debug. Verify to trust. A decoded claim is a hint about what the issuer intended, not proof of anything, until the signature has been checked server-side.

Safe handling while debugging

A JWT is a bearer credential: whoever holds it can use it until it expires. Paste tokens only into tools that run in the browser and do not transmit them. Prefer expired tokens or ones from a staging environment when sharing in tickets. Strip the signature segment before posting a token in chat; the first two segments still show the claims and the token is no longer usable.

More decoding and inspection tools are in the developer category.

Try it

Paste a token into the JWT Decoder to see the header, every claim with a short explanation, the signing algorithm and how long until exp. Nothing leaves your browser.

Tools in this guide

Frequently asked questions

Why does every JWT start with eyJ?

Because the header is JSON that begins with a curly brace and a double quote, and the characters {" encode to eyJ in Base64. The same prefix usually appears at the start of the payload segment too.

Can I verify a JWT signature in the browser?

You can only verify an RS256 or ES256 token if you have the issuer's public key. HS256 tokens need the shared secret, which should never be in a browser. In practice, verify on the server with a maintained library.

Is the exp claim in seconds or milliseconds?

Seconds since the Unix epoch, per RFC 7519. A 13-digit value is a millisecond timestamp and indicates a bug on the issuing side.

Is it safe to paste a JWT into an online decoder?

Only if the decoder runs client-side and sends nothing to a server. Even then, prefer expired or staging tokens, because a live JWT is a bearer credential that works for anyone who holds it.

More developer guides

JSON Formatter
Developer · Format, validate and minify JSON with syntax highlighting.
↵
Base64 Encode / Decode
Developer · Encode text to Base64 or decode Base64 back to plain text.
↵
URL Encoder / Decoder
Developer · Encode or decode URL components and query strings.
↵
Hash Generator
Developer · Generate MD5, SHA-1, SHA-256 and SHA-512 hashes.
↵
UUID Generator
Developer · Generate UUIDs v4 (random) in bulk with one click.
↵
Regex Tester
Developer · Test regular expressions against text with live match highlighting.
↵
Word Counter
Text · Count words, characters, sentences, paragraphs and reading time.
↵
Case Converter
Text · Convert text between UPPER, lower, Title, camelCase, snake_case and more.
↵
List Sorter
Text · Sort, reverse, deduplicate and shuffle lists of text.
↵
Markdown Preview
Text · Write Markdown and see a live rendered preview side by side.
↵
Percentage Calculator
Numbers · Calculate percentages, increases, decreases and what % one number is of another.
↵
Color Converter
Colors · Convert colors between HEX, RGB, HSL and HSV instantly.
↵
Snake
Arcade · Eat food, grow longer, don't hit the walls. A timeless classic.
↵
2048
Puzzle · Slide tiles and combine them to reach the legendary 2048 tile.
↵
Word Grid
Puzzle · Guess the hidden five letter word in six tries using color clues.
↵
Sudoku
Puzzle · Fill the 9x9 grid with logic. Four difficulties from easy to expert.
↵
Minesweeper
Strategy · Use number clues to clear the minefield. Classic logic, three levels.
↵
Breakout
Arcade · Smash every brick with paddle and ball. Power-ups, combos, endless levels.
↵
Typing Practice
Casual · Build your WPM and accuracy with timed drills from 30 seconds to 5 minutes. Find your weak keys.
↵
TypeRunner
Arcade · Multiplayer typing race. Race friends by link, beat computer runners, or chase your own ghost.
↵
Typing Blocks
Arcade · Type each word before its wooden block lands. Miss, and the pile climbs toward the line.
↵
Bubble Shooter
Arcade · Aim, bank shots off the walls, and pop groups of three or more bubbles.
↵
Road Rush
Arcade · Race seven rivals through traffic and punch your way to the front.
↵