JWT Authentication in Python — Core Concepts
What JWT authentication solves
Traditional session-based auth stores login state on the server. Every request triggers a database lookup: “Is session ID abc123 still valid?” That works, but it puts pressure on the database and makes horizontal scaling harder — every server needs access to the same session store.
JWT flips the model. Instead of the server remembering who you are, it gives you a signed token that you carry. Each request includes the token, and any server can verify it using a shared secret key. No database lookup required.
Anatomy of a JWT
A JWT has three parts separated by dots:
Header — declares the token type and signing algorithm (e.g., HS256).
Payload — contains claims: data like user ID, roles, and expiration time. Standard claims include sub (subject), exp (expiration), iat (issued at), and iss (issuer).
Signature — a cryptographic hash of the header and payload using a secret key. If anyone changes the payload, the signature won’t match, and the server rejects the token.
The whole thing is base64url-encoded, not encrypted. Anyone can decode the payload and read it. The signature only proves the data hasn’t been tampered with.
How Python handles JWTs
The PyJWT library is the standard choice. Encoding creates a token from a dictionary:
You provide the payload (user data plus expiration), choose an algorithm, and supply a secret. The library returns a compact string. Decoding reverses the process — it verifies the signature, checks expiration, and returns the original payload. If anything is wrong, it raises an exception.
Stateless vs. stateful tradeoffs
Stateless JWTs have a real weakness: you can’t revoke them before they expire. If a user logs out or gets banned, their token keeps working until the clock runs out.
Teams handle this with short expiration times (15-30 minutes) plus a refresh token pattern. The short-lived access token handles API calls; the longer-lived refresh token (stored securely) fetches new access tokens when needed.
For apps that need instant revocation — financial platforms, admin dashboards — some teams maintain a small blocklist of invalidated tokens. This reintroduces a database check, but only for the blocklist, not for every request.
Common misconception
Many developers treat JWTs as encrypted and store sensitive data in the payload. JWTs are signed, not encrypted. Anyone with the token can decode and read the payload. Never put passwords, credit card numbers, or private details in a JWT unless you also encrypt it (JWE), which is a separate standard.
Token storage matters
Where the client stores the JWT affects security significantly. Browser localStorage is convenient but vulnerable to cross-site scripting (XSS) — any injected script can steal it. HTTP-only cookies are safer because JavaScript can’t access them, though they introduce CSRF considerations. Mobile apps typically use secure storage APIs provided by the platform.
When to use JWTs
JWTs shine in microservices and APIs where multiple services need to verify identity without sharing a session store. They’re also common in single-page applications and mobile apps. For a traditional server-rendered website with one backend, session cookies might be simpler and equally effective.
The one thing to remember: JWTs let any server verify a user’s identity without a database call, but they trade revocability for that convenience — plan your expiration and refresh strategy carefully.
See Also
- Python Api Key Management Why apps use special passwords called API keys, and how to keep them safe — explained with a library card analogy
- Python Attribute Based Access Control How apps make fine-grained permission decisions based on who you are, what you're accessing, and the circumstances — explained with an airport analogy
- Python Audit Logging Learn Audit Logging with a clear mental model so your Python code is easier to trust and maintain.
- Python Bandit Security Scanning Why Bandit Security Scanning helps Python teams catch painful mistakes early without slowing daily development.
- Python Clickjacking Prevention How invisible website layers trick you into clicking the wrong thing, and how Python apps stop it