Uttir
By Uttir 6 min read

How to Read a JWT Token (Without Trusting the Sender)

A JWT is three base64 strings joined by dots. The middle one is the payload, and it is usually readable without any verification. The header tells you the algorithm. The signature is what you would verify to trust the contents. Here is how to read each part, with worked examples.

A JWT has three parts joined by dots: header.payload.signature. Each part is base64url-encoded. The header (first part) tells you the algorithm and key type. The payload (middle part) is the actual claim data — user ID, expiration, roles, etc. — and is usually NOT encrypted, just encoded. Anyone with the token can read it. The signature (last part) is what you would verify to trust the contents. To read a token safely, paste it into the <a href="/jwt-decoder">JWT Decoder</a> on this site. The decoding happens in your browser, not on a server, so the token never leaves your device.

A JWT (JSON Web Token) is a way to package a set of claims — "this user is logged in", "this token expires at midnight", "this user has admin role" — into a single string that a server can verify. The string is three base64url-encoded JSON objects joined by dots. If you have a JWT, you can read the claims yourself without trusting the server that gave it to you. This is useful for debugging, for understanding what an app actually stores about you, and for spotting when a token contains more than it should.

This is the practical guide. After reading it, you will be able to read any JWT in 30 seconds.

The three parts of a JWT

A JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJBbGljZSIsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Three parts, separated by dots:

  1. Header — the first part. Tells you the algorithm and token type.
  2. Payload — the middle part. The actual claim data. This is what you usually want to read.
  3. Signature — the last part. A cryptographic signature over the header and payload. You use this to verify the token came from the expected issuer.

All three parts are base64url-encoded (not encrypted — just encoded). You can decode any of them with a base64 decoder. The JWT Decoder on this site does all three in one click.

Reading the header

The first part is the header. It usually looks like this when decoded:

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

The two fields you care about:

  • alg — the algorithm used to sign the token. HS256 means HMAC with SHA-256 (a shared secret). RS256 means RSA signature (a public/private key pair). none means no signature (a security antipattern — see below).
  • typ — the token type. Almost always JWT.

If the algorithm is none, the token is unsigned. Anyone can forge it. This is a known JWT vulnerability and most modern libraries reject none by default, but a poorly-written service might accept it. If you see a token with alg: none, that is a red flag.

If the algorithm is something unexpected (like HS1 or a non-standard name), that is also a red flag. The common algorithms are HS256, HS384, HS512 (HMAC variants), RS256, RS384, RS512 (RSA variants), ES256, ES384, ES512 (ECDSA variants), and EdDSA. Anything else is suspicious.

Reading the payload (the part you usually want)

The middle part is the payload. This is the actual data the token carries. A typical payload:

{
  "sub": "12345",
  "name": "Alice",
  "iat": 1516239022,
  "exp": 1516242622,
  "role": "user"
}

The fields depend on the service. Common standard fields:

  • sub — the subject. Usually the user ID. sub: "12345" means the token represents user 12345.
  • iat — issued at. The Unix timestamp when the token was created. 1516239022 is Jan 17, 2018.
  • exp — expiration. The Unix timestamp when the token stops being valid.
  • nbf — not before. The Unix timestamp when the token becomes valid. Optional.
  • iss — issuer. Who created the token. Optional.
  • aud — audience. Who the token is for. Optional.
  • jti — JWT ID. A unique identifier for the token. Optional.

Beyond these standard fields, services add their own. Common custom fields include:

  • name, email, picture — user profile data
  • role, roles, permissions — authorization data
  • org, tenant, workspace — multi-tenant context
  • session_id — to invalidate the session server-side

Read this part carefully. It tells you exactly what the server knows about you, what you can do (your role), and how long you stay logged in (the expiration).

Reading the signature (the part that proves trust)

The last part is the signature. It is a base64url-encoded HMAC or RSA signature over the first two parts (header.payload). The signature is what you would verify to trust the contents of the token.

You cannot read the signature directly — it is a hash, not a structured value. To verify a signature, you need the secret (for HMAC) or the public key (for RSA), and you run the same algorithm to check whether the signature matches. The browser, the server, and the library do this for you; you do not need to do it by hand.

The important point: the signature is what makes the token trustworthy. If you only read the payload without verifying the signature, you have no proof the token was issued by the service that claims to have issued it. Anyone can write a JWT with any payload they want — what stops them is the signature, which requires the secret or private key.

What you can and cannot learn from a JWT

You CAN learn, just by reading the payload:

  • What user the token represents
  • When the token expires
  • What permissions the token has
  • What other data the service stored about you in the token

You CANNOT learn, just by reading the payload:

  • Whether the token is valid (was it actually issued by the right service?)
  • Whether the token has been revoked (a service can keep a list of revoked tokens even if the signature is valid)
  • Whether the token has been tampered with (this is what the signature prevents)

So reading a JWT payload is good for UNDERSTANDING what the token says, but it is not enough to TRUST the token. Trust comes from verifying the signature against the right key.

Common JWT patterns and what to look for

Long expiration times

If a JWT has an expiration days or weeks in the future, that is a security smell. Best practice is short-lived access tokens (5-60 minutes) with a separate refresh token. Long-lived JWTs are convenient but a security risk: if stolen, the attacker has access for a long time.

Common bad patterns:

  • "exp": 18446744073709552 — far-future timestamp, effectively never expires. If you see this, the service is being lazy about token rotation.
  • "exp" more than 24 hours in the future — too long for an access token. Acceptable for a refresh token, not for the access token itself.

Too much personal data in the payload

JWTs are URL-safe and travel in HTTP headers, so the payload is logged in web server logs, browser history, and analytics tools. If a JWT contains your email, your full name, your home address, and your phone number, that data is being spread around in places you don't know about.

Common bad patterns:

  • Sensitive data (SSN, payment info, address) in the payload
  • Password hashes, even hashed, in the payload
  • Internal IDs (like database row IDs) that an attacker could use to enumerate users

The fix is to put a user ID in the token and have the server look up the rest of the data on each request. The token is an identifier, not a data dump.

The "alg: none" antipattern

As mentioned above, if the header says "alg": "none", the token is unsigned. A service that accepts unsigned tokens is broken. The original JWT spec allowed this for testing; most modern libraries reject it; a service that still accepts it is asking to be hacked.

If you find a token with alg: none in production, that is a critical vulnerability. The fix is to update the library to a version that rejects none, and to require a real signature on all tokens.

Algorithm confusion

Another classic JWT attack: the server expects RS256 (public key) but the attacker sends a token with HS256 using the public key as the HMAC secret. If the server does not check the algorithm, it will accept the forged token. This is the "algorithm confusion" attack.

Modern libraries defend against this by pinning the expected algorithm. If you are implementing JWT verification, pin the algorithm explicitly. Do not trust the alg field in the header to decide which algorithm to use for verification.

How to read a JWT safely (the practical workflow)

To read a JWT, paste it into the JWT Decoder on this site. The decoding happens in your browser, so the token never leaves your device. You see the header, the payload, and the signature, all in one view.

For a deeper dive, you can also use the Base64 Decoder on each part manually. Split the token on dots, decode each part with base64, and read the JSON. The JWT Decoder does this for you automatically.

If the token's JSON is minified, paste it into the JSON Formatter to make it readable. If you want to check whether the JSON is well-formed, the JSON Validator catches malformed JSON.

The honest summary

A JWT is three base64url-encoded JSON parts joined by dots. The header tells you the algorithm. The payload is the data — readable by anyone with the token, so do not put secrets in it. The signature is what makes the token trustworthy. To read a token safely, paste it into the JWT Decoder on this site. The decoding happens in your browser, not on a server, so the token never leaves your device. Read the payload to understand what the service stores about you, but verify the signature (or trust the server to do it) before relying on the contents. The long-expiration antipattern, the too-much-personal-data antipattern, the alg: none antipattern, and the algorithm-confusion attack are the four things to watch for when reading JWTs in the wild.

#jwt#security#authentication#how-to#developer-tools

New tools and guides, once a week

One short email when something new ships. No tracking, no images, unsubscribe with one click.