What Is a JWT? β Definition and How It Works
Learn how a JWT is structured, why base64url is not encryption, and the validation mistakes that break security.
A JSON Web Token is a signed, base64url-encoded token carrying claims. It is the standard bearer token format for APIs, and the most common thing you will find in an Authorization header.
1. The Structure
Three dot-separated parts:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJleHAiOjE3ODcwMDAwMDB9.<signature>
header payload signature
{"alg": "HS256", "typ": "JWT"}
{"sub": "123", "iss": "example.com", "exp": 1787000000, "iat": 1786996400}
2. Encoded Is Not Encrypted
The single most important fact. The header and payload are base64url β readable by anyone holding the token.
import base64, json
part = token.split('.')[1]
payload = json.loads(base64.urlsafe_b64decode(part + '=' * (-len(part) % 4)))
The signature proves the token was not altered. It does nothing to hide the contents.
So a JWT must never carry anything confidential. Putting a password, a full personal record, or an internal secret in a payload is exposing it to every holder of the token β a mistake made regularly.
3. The Standard Claims
| Claim | Meaning |
|---|---|
iss |
Issuer |
sub |
Subject β usually the user id |
aud |
Intended audience |
exp |
Expiry, as a Unix timestamp |
nbf |
Not valid before |
iat |
Issued at |
jti |
Unique token id, for revocation lists |
4. Validation Mistakes That Matter
Trusting alg from the header. The classic vulnerability: a token declaring "alg": "none", or switching RS256 to HS256 so the public key is used as an HMAC secret. Always pin the expected algorithm server-side rather than reading it from the token.
Not verifying the signature at all. Decoding and reading claims without verification accepts any forged token.
Ignoring exp. An unchecked expiry makes tokens valid forever.
Not checking aud or iss. A token issued for a different service may otherwise be accepted.
Clock skew handled badly. A little tolerance is reasonable; a large one extends every token's life.
import jwt
claims = jwt.decode(token, key, algorithms=["RS256"], # pinned, not from the header
audience="my-api", issuer="https://issuer.example.com")
5. Why They Are Hard to Revoke
A JWT is self-contained: the server validates it by checking a signature, without consulting a database. That is the performance advantage and the operational weakness.
Because nothing is looked up, there is no natural way to invalidate one early. The mitigations all reintroduce state: short expiry with refresh tokens, a jti denylist, or a token version claim checked against the user record.
Short-lived access tokens plus refresh tokens is the usual answer, which means the token you hold expires quickly by design.
6. Meeting Them in Collection Work
JWTs appear constantly in a site's own API calls, and the practical notes are:
- Read the payload to find
exp. That tells you how long a captured token remains usable, which is usually minutes to hours. - Do not treat a token as a durable credential. It expires, and re-obtaining it usually requires the authentication flow.
- Never log or commit one. It is a live credential; base64 is not redaction.
- Expect a refresh flow. Long-running collection against an authenticated API needs to handle refresh, under a lock so concurrent workers do not race and invalidate each other.
- Do not attempt to forge one. Without the signing key you cannot, and attempting it against someone else's service is unauthorised access.
7. JWT Versus Opaque Tokens
| JWT | Opaque token | |
|---|---|---|
| Contents | Readable claims | Meaningless string |
| Validation | Signature check, stateless | Database lookup |
| Revocation | Hard | Trivial |
| Size | Larger | Small |
| Leaks information | Yes, to any holder | No |
Opaque tokens are the better choice when revocation matters more than statelessness β which, for many systems, it does.
8. Summary
A JWT is a base64url-encoded, signed set of claims: readable by anyone who holds it, tamper-evident but not confidential, and validated without a database lookup.
Pin the expected algorithm server-side rather than trusting the token's own alg, always verify the signature and check exp, aud, and iss β and treat any token you encounter as a short-lived live credential that must never be logged or committed.