JWT (JSON Web Token)
A JSON Web Token is a compact, URL-safe string that carries a set of claims as JSON, usually signed so the receiver can detect tampering. It is defined in RFC 7519 and is widely used for API and session authentication.
What a JWT looks like
A JWT is three Base64URL-encoded segments joined by dots:
header.payload.signature
- The header says how the token is signed, for example
{"alg":"HS256","typ":"JWT"}. - The payload holds the claims, the statements the issuer is making, for example
{"sub":"1234567890","name":"Ada Lovelace","iat":1516239022}. - The signature is computed over the first two segments with a secret or a private key. A receiver with the matching secret or public key can check that nobody changed the header or payload after signing.
Because each of the first two segments is just Base64URL-encoded JSON, anyone holding the token can read them. Nothing needs to be cracked.
Registered claims
RFC 7519 defines a handful of standard claims. All are optional, but most real tokens use several:
| Claim | Meaning |
|---|---|
iss | Issuer: who created the token |
sub | Subject: who the token is about, often a user ID |
aud | Audience: who the token is intended for |
exp | Expiration time: the token must not be accepted after this |
nbf | Not before: the token must not be accepted before this |
iat | Issued at: when the token was created |
jti | JWT ID: a unique identifier, useful for revocation lists |
The three time claims are numeric dates: seconds since the Unix epoch, not milliseconds. See Unix timestamp.
Signed, not encrypted
Most JWTs in the wild are JWS tokens (RFC 7515): signed, so tamper-evident, but with a readable payload. There is also JWE (RFC 7516), which encrypts the payload, but it is much less common. If you are not sure which you have, count the segments: a signed JWT has three, and a JWE has five.
The practical rule: never put anything in a JWT payload that the token holder should not be allowed to read.
Common pitfalls
- Treating a decoded token as a verified one. Decoding shows what the token claims. Only checking the signature shows the claims are genuine. Anyone can build a token that says
"admin": true. - Trusting the
algheader. The header is attacker-controlled. Servers should verify with the algorithm they expect, and rejectnoneunless they explicitly intend to allow unsigned tokens. Confusing an RSA public key for an HMAC secret is a classic algorithm-confusion attack. - Milliseconds instead of seconds. Putting
Date.now()intoexpyields a token that expires roughly 50,000 years from now. - Ignoring clock skew. Servers’ clocks drift, so libraries usually allow a small leeway on
expandnbf. - Long-lived tokens with no revocation. A signed token stays valid until
expunless the server keeps a denylist, so short lifetimes plus refresh tokens are the usual pattern.
Inspecting one safely
Real tokens are credentials, so pasting one into a server-side decoder means handing that server a working key to your account. The decoder here runs in your browser and never sends the token anywhere. It reads the header, payload and expiry only, and deliberately does not verify signatures, because that would require your secret.
Related terms
- Base64URL — Base64URL is a variant of Base64 that swaps the characters "+" and "/" for "-" and "_" and usually drops the "=" padding, so encoded bytes can sit safely inside URLs, filenames and JWTs. It is defined in RFC 4648, section 5.
- Unix timestamp — A Unix timestamp is the number of seconds that have passed since 00:00:00 UTC on 1 January 1970, the Unix epoch. It is a single number that identifies a moment in time regardless of time zone, which makes it easy to store, sort and compare.
- SHA-256 (Secure Hash Algorithm, 256-bit) — SHA-256 is a cryptographic hash function from the SHA-2 family that turns any input into a fixed 256-bit digest, usually written as 64 hexadecimal characters. The same input always gives the same output, and it is infeasible to work backwards from the digest.
References
Ads on this page
Non-personalized ads help keep Vaultools free — Google decides where they appear on the page.
Go Pro to remove them →