JWT
What is JWT (JSON Web Token)? The Complete Guide
5 min read
What is JWT (JSON Web Token)? The Complete Guide
If you are a web developer building modern applications, especially single-page applications (SPAs) like React or Vue, or connecting to REST APIs, you have almost certainly encountered JWT (pronounced "jot").
JWT stands for JSON Web Token. It has rapidly become the industry standard for securing APIs and authenticating users across distributed systems. But how exactly does a string of seemingly random characters keep an entire user session secure? Why did the industry move away from traditional session cookies toward these tokens?
In this exhaustive guide, we will break down the mechanics of JSON Web Tokens, explore their internal structure, compare them to traditional authentication methods, and discuss the critical security best practices you must follow to prevent your JWTs from being compromised.
The Problem: Traditional Stateful Authentication
To understand why JWT was invented, you have to understand the problem it solves.
HTTP is a stateless protocol. This means that every time a user makes a request to a server (like clicking a link or submitting a form), the server has no memory of the previous request. If you log in on page 1, the server will immediately forget you are logged in by the time you navigate to page 2.
Historically, developers solved this using Session Cookies (Stateful Authentication).
- The user logs in with a username and password.
- The server verifies the credentials and creates a "Session" in its database (or memory), generating a unique
Session ID. - The server sends this
Session IDback to the user's browser, which stores it in a cookie. - On every subsequent request, the browser sends the cookie. The server looks up the
Session IDin its database, sees who it belongs to, and allows access.
Why did this break?
This system worked perfectly for 15 years. But then, modern web architecture changed. Applications shifted from monolithic servers to microservices.
Imagine an e-commerce site where the "User Server" handles logins, the "Product Server" handles the catalog, and the "Payment Server" handles checkout. If the User Server creates a session in its local database, the Product Server has no idea who the user is because it cannot see the User Server's database. Sharing session states across dozens of distributed servers is incredibly slow, expensive, and difficult to scale.
The Solution: Stateless Authentication with JWT
JSON Web Tokens (JWT) provide a stateless solution.
Instead of storing a session in a database and sending the user a meaningless ID, the server packages all the necessary user information (like their User ID and role) into a small JSON object. The server then digitally signs this JSON object using a secret cryptographic key, and sends the entire signed object back to the user. This is the JWT.
When the user makes their next request, they send the JWT along with it. The receiving server looks at the token, verifies the digital signature to ensure it has not been tampered with, and immediately knows who the user is—without ever having to look up a database!
Because the token itself contains the data, and the signature guarantees its authenticity, any microservice that knows the server's secret key can verify the token instantly.
The Structure of a JWT
If you look at a raw JWT, it looks like a long string of random text separated by two periods:
xxxxxxx.yyyyyyy.zzzzzzz
These three sections are actually Base64-encoded strings representing the three parts of the token: the Header, the Payload, and the Signature.
1. The Header (xxxxxxx)
The header typically consists of two parts: the type of the token (which is "JWT") and the signing algorithm being used, such as HMAC SHA256 (HS256) or RSA.
{
"alg": "HS256",
"typ": "JWT"
}
This JSON is Base64Url encoded to form the first part of the token.
2. The Payload (yyyyyyy)
The payload contains the claims. Claims are statements about an entity (typically, the user) and additional data. There are three types of claims:
- Registered claims: Predefined claims recommended by the JWT standard. Examples include
iss(issuer),exp(expiration time),sub(subject/user ID), andaud(audience). - Public claims: Custom claims created by you, but they should be defined in a public registry to avoid collisions.
- Private claims: Custom claims created to share information specifically between your server and the client. For example,
"role": "admin".
{
"sub": "1234567890",
"name": "John Doe",
"role": "admin",
"iat": 1516239022,
"exp": 1516242622
}
This JSON is Base64Url encoded to form the second part of the token.
CRITICAL WARNING: The header and payload are only encoded (Base64), NOT encrypted. Anyone who intercepts a JWT can easily decode it and read the payload. Never put passwords, social security numbers, or sensitive financial data inside a JWT payload.
3. The Signature (zzzzzzz)
The signature is the most important part of the JWT. It is what prevents users from modifying their own tokens.
To create the signature, the server takes the encoded header, the encoded payload, and a highly secure Secret Key known only to the server. It runs these three pieces through the algorithm specified in the header (like HMAC SHA256).
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret_key
)
If a malicious user intercepts their JWT, decodes the payload, changes "role": "user" to "role": "admin", and re-encodes it, the server will reject it. Why? Because the hacker does not know the server's secret_key. When the server receives the modified token, it will recalculate the signature. The new signature will not match the old signature, and the server will instantly know the token was forged.
JWT Best Practices and Security Pitfalls
While JWTs solve the scaling problem of sessions, they introduce completely new security challenges. If you are implementing JWTs, you must follow these rules:
1. Keep the Lifespan Short (Expiration)
Because JWTs are stateless, there is no easy way to "revoke" or "destroy" a JWT from the server side. Once a server issues a JWT, it is valid until it expires. If a hacker steals a user's token, they have full access to that account.
Therefore, your JWTs should have a very short exp (expiration) claim—typically 15 minutes. To keep the user logged in without forcing them to re-enter their password every 15 minutes, you use a Refresh Token (a separate, long-lived token stored securely that can request new short-lived JWTs).
2. Store Tokens Securely
Where do you put the JWT in the browser?
- Local Storage / Session Storage: This is the most common, but also the most dangerous place. Any Javascript running on your page (including malicious scripts from XSS attacks) can read Local Storage and steal the token.
- HttpOnly Cookies: This is the recommended approach. If you send the JWT inside an
HttpOnlyandSecurecookie, the browser automatically sends it with every request, but Javascript is completely blocked from reading the cookie, defeating XSS attacks.
3. Validate the Signing Algorithm
Historically, some JWT libraries had a critical flaw where a hacker could change the header to "alg": "none" (no signature required). The server would blindly accept it. Always ensure your backend framework explicitly hardcodes the expected algorithm (e.g., HS256) and rejects "none".
Conclusion
JSON Web Tokens (JWT) have fundamentally changed how modern web applications handle authentication. By packaging identity and authorization data into a cryptographically verified token, they eliminate the need for centralized session databases, allowing microservices to scale infinitely and independently.
However, with great power comes great responsibility. Because JWTs are stateless and self-contained, mishandling their storage, making them last too long, or putting sensitive data in the payload can lead to catastrophic security breaches. Understand the structure, protect your secret keys, use HttpOnly cookies, and JWTs will serve as a robust, blazing-fast security layer for your applications.