If you have built or worked with any modern web application you have almost certainly encountered JWT tokens. They appear in API authentication headers, URL parameters, cookies, and local storage. They are the standard way that web applications verify identity in a stateless world. Yet many developers implement JWT authentication without fully understanding how JWTs work, what their security implications are, or when they should and should not be used. This guide explains everything you need to know about JWTs from first principles.
What is a JWT?
JWT stands for JSON Web Token, defined in RFC 7519. It is a compact, self-contained way to securely transmit information between parties as a JSON object. The information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with HMAC algorithms like HS256) or a public/private key pair (with RSA or ECDSA algorithms like RS256 or ES256). The key property of a JWT is that it is self-contained — all the information needed to understand the token's context is inside the token itself, without requiring a database lookup.
The Three Parts of a JWT
A JWT looks like three Base64url-encoded strings separated by dots. The first part is the Header — a JSON object containing the token type (always JWT) and the signing algorithm used (such as HS256, RS256, or ES256). The second part is the Payload — a JSON object containing the claims, which are statements about the subject and additional metadata. The third part is the Signature — computed by encoding the header and payload and signing them with a secret key, used to verify the token has not been tampered with.
Remember: the JWT payload is Base64url encoded, not encrypted. Anyone who has the token can decode and read the payload in seconds. Never put sensitive data like passwords in a JWT payload.
Standard JWT Claims You Should Know
- `sub` (Subject) — identifies the principal the token is about, usually a user ID like `user_12345`
- `iss` (Issuer) — identifies who issued the token, typically the URL of your authentication server
- `aud` (Audience) — identifies the intended recipients of the token, your application should verify this matches
- `exp` (Expiration Time) — a Unix timestamp after which the token is invalid and must be rejected
- `iat` (Issued At) — a Unix timestamp of when the token was created
- `nbf` (Not Before) — a Unix timestamp before which the token must not be accepted
- `jti` (JWT ID) — a unique identifier for the token, useful for preventing replay attacks
How JWT Authentication Works in Practice
The typical JWT authentication flow works as follows. The user logs in with their credentials. The server validates the credentials and generates a JWT containing the user's ID and other relevant claims, signs it with a secret key, and returns it to the client. The client stores the JWT (in a cookie or memory) and includes it in the Authorization header of subsequent requests: `Authorization: Bearer [token]`. The server receives the request, extracts the JWT, verifies the signature, checks the expiry, and if everything is valid, processes the request without ever consulting a database — the user's identity is proven by the token itself.
JWT vs Session-Based Authentication
- Session-based auth stores session data on the server — the client gets only a session ID. JWT auth stores all data in the token on the client — the server is stateless.
- Sessions require a database lookup on every request to verify the session. JWTs only require signature verification — no database needed.
- Sessions can be invalidated instantly by deleting them from the database. JWTs cannot be truly invalidated before expiry without a blocklist (which reintroduces statefulness).
- JWTs are better suited for microservices and distributed systems where multiple services need to verify identity without sharing a session store.
- Sessions are better when you need the ability to instantly revoke access, such as after a password change or account suspension.
JWT Security Best Practices
- Always verify the signature on the server — never trust a JWT without verifying the signature first
- Set short expiration times — 15 minutes to 1 hour for access tokens is common
- Use refresh tokens for long-lived sessions instead of long-lived access tokens
- Store JWTs in httpOnly cookies to protect against XSS attacks — not in localStorage
- Always use HTTPS — never transmit tokens over unencrypted connections
- Validate the iss and aud claims to prevent tokens from one service being used on another
- Implement a token blocklist if you need the ability to immediately invalidate tokens
Never store sensitive data like passwords, credit card numbers, or API secrets in a JWT payload. The payload is only encoded, not encrypted — anyone with the token can read it.