A JSON Web Token (JWT, pronounced "jot") is a compact, signed string that carries a few facts, called claims, from one party to another. Servers hand them out after you log in, and your app sends the token back with each request to prove who you are, without the server keeping a session. Decode any token with the JWT Decoder, or make one with the JWT Generator & Verifier.
Three parts, separated by dots
A JWT looks like header.payload.signature. Each part is Base64 in its URL-safe form. Here is a real token made with the secret secret:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFuYSIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoxNzAwMDAzNjAwfQ.K5CXsF2jBQ9-Pr4xg7yeVj3Vr-Be0jvTHInQnYK-aSk
Header (eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9) decodes to:
{"alg":"HS256","typ":"JWT"}
Payload (eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFuYSIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjoxNzAwMDAzNjAwfQ) decodes to:
{"sub":"1234567890","name":"Ana","iat":1700000000,"exp":1700003600}
The signature (K5CXsF2jBQ9-Pr4xg7yeVj3Vr-Be0jvTHInQnYK-aSk) is computed over the first two parts with the algorithm and key named in the header. HS256 is HMAC with SHA-256: whoever knows the secret can both create and check it.
Registered claims
| Claim | Meaning |
|---|---|
iss |
Issuer: who created the token |
sub |
Subject: who the token is about, usually a user ID |
aud |
Audience: which service the token is meant for |
exp |
Expiration time (Unix seconds); reject the token after it |
nbf |
Not before: reject the token until this time |
iat |
Issued at: when the token was created |
jti |
JWT ID: a unique identifier, useful for revocation lists |
Times are in seconds since 1970 (see the Unix Timestamp Converter): the token above was issued at 1700000000, which is 14 November 2023, and expires an hour later.
How a server checks a token
- Split the token into its three parts and decode the header.
- Recompute the signature over
header.payloadwith the expected algorithm and key, and compare it with the one in the token. A different secret fails: the token above verifies withsecretand is rejected with anything else. - Check the claims:
exphas not passed,nbfhas, andissandaudare the expected values.
Signing algorithms
- HS256 / HS384 / HS512 use one shared secret. Simple, but every service that verifies tokens can also forge them.
- RS256 / ES256 use a private key to sign and a public key to verify, so you can share the public key widely. Prefer these when many services verify tokens issued by one.
Pitfalls
- A JWT is signed, not encrypted. Anyone can read the payload by decoding it. Never put passwords or secrets in it.
- Always fix the algorithm on the server. Accept only the algorithm you expect. Trusting the
algfield has led to attacks, such as the header{"alg": "none"}that some libraries once accepted as "no signature required". - Set a short
exp. A stolen token works until it expires. Use short lifetimes plus refresh tokens. - A token cannot be revoked by itself. Once issued it stays valid until
exp. If you need instant logout, keep a denylist ofjtivalues or use server-side sessions instead. - Store tokens carefully. Tokens in
localStorageare readable by any script on the page, so an XSS bug exposes them; anHttpOnlycookie is harder to steal. - Use a long random secret for HMAC, not a word like
secret. The example here is for demonstration only.