JWT-Based Authentication
JWT (JSON Web Token) is a self-contained token. It carries the user's info inside it, plus a signature that proves the server issued it and nobody changed it. Because of this, the server can verify the token without any database lookup.
What a JWT Looks Like
A JWT is three parts joined by dots:
Important: Base64 is encoding, not encryption. Anyone can decode and read the payload. Never put passwords or secrets in it.
Common Claims
| Claim | Meaning |
|---|---|
| sub | Subject, the user ID |
| exp | Expiry time |
| iat | Issued at time |
| iss | Issuer, who created the token |
| aud | Audience, who the token is meant for |
You can add your own claims too, like role.
How the Signature Protects the Token
Think of a sealed envelope with a wax stamp. Anyone can read the letter's cover, but if someone changes it, the seal breaks.
1Attacker changes payload: "role": "CUSTOMER" → "role": "ADMIN"2 3Server recomputes signature from the new header + payload4→ it doesn't match the signature in the token5→ token rejected ✘The attacker can't make a matching signature because they don't have the secret key.
Signing Algorithms
| HS256 (Symmetric) | RS256 (Asymmetric) | |
|---|---|---|
| Keys | One shared secret | Private key signs, public key verifies |
| Who can verify | Only services with the secret | Anyone with the public key |
| Risk | Every service holding the secret could also create tokens | Only the auth server can create tokens |
| Best for | Single app / monolith | Microservices, third-party verification |
In microservices, RS256 is preferred: the auth service keeps the private key, and every other service just uses the public key.
How It Works
The Order Service never calls the Auth Service or a database. It trusts the signature.
Validation Checklist
On every request, the server checks:
- Signature is valid.
- alg is the expected one (reject "none").
- exp is in the future.
- iss and aud match what's expected.
The Revocation Problem
A JWT stays valid until it expires. If a user logs out or gets banned, the token still works. Common fixes:
| Approach | How | Trade-off |
|---|---|---|
| Short expiry | Access token lives 5–15 min | Small window of risk |
| Refresh tokens | Long-lived, stored server-side, revocable | Extra endpoint and logic |
| Denylist | Store revoked token IDs (jti) in Redis | Adds a lookup, partly stateful again |
| Token version | Store a version per user; bump it to kill all tokens | Lookup per request |
Most systems use short access tokens + refresh tokens.
Security Pitfalls
- Treating the payload as secret (it's readable).
- Accepting alg: none or letting the token choose the algorithm.
- Long-lived access tokens.
- Weak HS256 secrets that can be brute-forced.
- Putting too much data in the token (it's sent on every request).
Pros and Cons
| Pros | Cons |
|---|---|
| No lookup needed, fast | Can't revoke before expiry (without extra work) |
| Scales across many servers | Payload is readable by anyone |
| Works across services and domains | Larger than a session ID |
| Standard format, many libraries | Stale data: role changes apply only after a new token |
When to Use It
- Microservices that verify users independently
- Mobile apps and SPAs calling APIs
- Systems where one auth server issues tokens for many services
Avoid it when you need instant revocation on every request; sessions fit better there.
Summary
Next, we'll look at OAuth 2.0, which lets users give an app access to their data on another service without sharing their password.