TL;DR
A JWT is a base64-encoded JSON object containing claims about the user. It is signed but not encrypted by default. Anyone who holds the token can read its payload. Verify the algorithm and expiry before trusting one.
If you have built or used a modern web app, you have almost certainly dealt with JSON Web Tokens. They show up as the Authorization: Bearer header on API requests, as cookies, or tucked inside local storage. They look like a random string of characters. They are not.
A JWT is a structured piece of data. Once you know how to decode one, you can read exactly what it contains in about ten seconds.
The Three Parts
A JWT is three base64url-encoded strings joined by dots: header.payload.signature
Header: A JSON object describing the token. The most important field is alg, which specifies the algorithm used to sign the token. Common values are HS256 (HMAC with SHA-256, a symmetric algorithm using a shared secret) and RS256 (RSA with SHA-256, an asymmetric algorithm using a public/private key pair). There is also none, which means no signature at all.
Payload: The actual claims. This is JSON data about the user and the session. Standard fields include sub (subject, usually the user ID), iat (issued at, a Unix timestamp), exp (expiry, another Unix timestamp), and iss (issuer, who created the token). Applications add their own custom fields here: roles, permissions, email addresses, plan names.
Signature: A cryptographic signature computed over the header and payload. The server uses this to verify that neither part has been tampered with. Without a valid signature, the token should be rejected.
The Part People Get Wrong: It Is Not Encrypted
Base64url encoding is not encryption. It is just a way of representing binary data as ASCII characters. Anyone who gets hold of a JWT can decode the header and payload immediately, without any key. If your app puts sensitive data in the payload -- an admin flag, a full email address, an internal user ID -- that data is visible to anyone who holds the token.
The signature verifies integrity. It does not provide confidentiality. If you need the payload to be secret, you need a JWE (JSON Web Encryption) rather than a plain JWT.
The alg:none Attack
This is a classic vulnerability that still appears in real applications. The JWT specification allows a token with "alg": "none" in the header, meaning the token is unsigned. Some libraries, when they see this, skip signature verification entirely.
An attacker can take a legitimate token, decode it, modify the payload (changing their role from 'user' to 'admin', for example), set the algorithm to 'none', remove the signature, and submit it. If the server accepts it, the attacker has elevated their own privileges without knowing the signing key.
The fix is simple: explicitly allowlist the algorithms your application accepts. Never allow none. Never accept an algorithm from the token itself without checking it against your configured list.
The Expiry Problem
A JWT with an exp field will be rejected by any library that properly validates tokens after that timestamp passes. A JWT without an exp field is valid forever.
Forever-valid tokens are a significant security risk. If one is stolen -- from a log file, from local storage via XSS, from a database breach -- the attacker can use it indefinitely. Always set an expiry. For sensitive applications, keep it short (15 minutes to an hour) and use a refresh token pattern to issue new access tokens without requiring the user to log in again.
What the JWT Decoder Tool Shows You
The JWT Decoder on this site decodes any token and shows you the header and payload in readable JSON. It also flags specific issues automatically: alg:none tokens, missing expiry fields, tokens that have already expired, and sensitive-looking data in the payload (email addresses, anything labeled 'password', admin flags).
Paste a token you have found in a request, in local storage, or in an app you are reviewing. You will see its contents within a second. No data is sent to any server -- the decoding happens in your browser.
The Key Takeaway
JWTs are not opaque tokens. They are readable by anyone who has them. Build your applications with that assumption. Keep payloads minimal. Set expiry. Lock down the algorithms your server accepts. And use the decoder whenever you want to quickly understand what a token actually contains.
Khodor Ghalayini
Engineer · AI Builder · Cybersecurity Practitioner
Engineer with 10+ years in systems and project management. I build AI-powered tools to help people work smarter — starting with the security and productivity problems I've personally run into. More about me →