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.

pythonsecurityauthenticationweb

See Also