In the modern world of web development, security and stateless authentication are essential. One of the most powerful tools used to implement secure and stateless authentication is JSON Web Tokens (JWT). Whether you are building an API for a mobile app or a web app with single-page architecture, JWTs offer a compact, URL-safe method for representing claims securely between two parties.
In this comprehensive guide, we’ll explore what JWTs are, how they work, and how to implement them in real-world applications.
What is a JSON Web Token (JWT)?
A JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed.
JWTs can be signed using a secret (with HMAC) or a public/private key pair (with RSA or ECDSA).
Structure of a JWT
A JWT is made up of three parts, separated by dots (.
):
xxxxx.yyyyy.zzzzz
- Header
The header typically consists of two parts: "alg": "HS256", "typ": "JWT" }
- Payload
The payload contains the claims. Claims are statements about an entity (typically, the user) and additional metadata. json "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
- Signature
To create the signature, you take the encoded header, the encoded payload, a secret, and the algorithm specified in the header, and sign that. scsHMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Why Use JWT?
- Stateless Authentication: No need to store sessions in the server.
- Cross-Domain Authentication: Easily passed between different services.
- Scalable: Since there’s no server-side session storage, it’s ideal for microservices and distributed systems.
- Mobile-Friendly: Easily used in mobile apps where cookies are hard to manage.
Common JWT Use Cases
- Authentication
- Authorization
- Information Exchange
- Single Sign-On (SSO)
- API Security
How JWT Authentication Works (Step-by-Step)
1. User Login
The user logs in by providing their credentials (e.g., username/password) to an endpoint.
2. Server Verifies Credentials
If the credentials are valid, the server generates a JWT and sends it back to the client.
3. Client Stores the JWT
The client (browser, mobile app, etc.) stores the token—usually in localStorage, sessionStorage, or cookies.
4. Client Sends JWT in Requests
For each subsequent request, the client includes the JWT in the Authorization
header:
Authorization: Bearer <token>
5. Server Verifies Token
The server decodes and verifies the token. If it’s valid, it grants access to the requested resource.
Implementing JWT in a Web Application
Example: Node.js + Express + jsonwebtoken
Step 1: Install Dependencies
npm install express jsonwebtoken body-parser
Step 2: Create a Basic Server
const express = require('express');
const jwt = require('jsonwebtoken');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
const SECRET_KEY = "your-256-bit-secret";
// Login Route
app.post('/login', (req, res) => {
const { username, password } = req.body;
// In production, verify username/password from DB
if (username === 'admin' && password === 'password') {
const token = jwt.sign({ username }, SECRET_KEY, { expiresIn: '1h' });
res.json({ token });
} else {
res.status(401).json({ message: 'Invalid credentials' });
}
});
// Protected Route
app.get('/protected', (req, res) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, SECRET_KEY, (err, user) => {
if (err) return res.sendStatus(403);
res.json({ message: 'Protected data', user });
});
});
app.listen(3000, () => console.log('Server started on http://localhost:3000'));
How to Test JWTs
You can use tools like:
- jwt.io
- Postman (for sending auth headers)
- Curl:
curl -H "Authorization: Bearer <token>" http://localhost:3000/protected
Security Best Practices for JWT
- Keep Your Secret Secret
Never expose the secret key used to sign JWTs. - Use HTTPS
Always use SSL/TLS to protect tokens in transit. - Set Expiration Times
Useexp
claim to limit the token’s lifetime. - Avoid Storing JWTs in LocalStorage (for sensitive data)
UseHttpOnly
cookies for better security against XSS. - Blacklist/Rotate Tokens
Implement token revocation strategies for logout or compromised tokens. - Validate All Claims
Especiallyiss
,aud
, andexp
claims.
Refresh Tokens
Access tokens should be short-lived. To maintain a session without re-login, use a refresh token:
- Client stores a long-lived refresh token securely.
- When access token expires, client sends refresh token to get a new access token.
- Refresh tokens are stored and validated on the server.
JWT vs Sessions: Key Differences
Feature | JWT | Sessions |
---|---|---|
Storage | Client | Server |
Scalability | Highly scalable | Limited by server memory |
Stateless | Yes | No |
Revocation | Harder (requires tracking) | Easy (just delete session) |
Security | Relies on secret/key | Relies on session ID |
JWT in Frontend Applications
When working with frameworks like React, Angular, or Vue:
- Store JWT securely (preferably HttpOnly cookies)
- Intercept requests and add the JWT in headers using Axios or Fetch
- Use guards/routes to protect pages
Example (React with Axios):
axios.interceptors.request.use(config => {
const token = localStorage.getItem("token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
Final Thoughts
JWT is a powerful tool for handling stateless authentication and securely transmitting information between parties. When implemented correctly, it can offer a fast, scalable, and secure solution for authentication in modern applications.
However, JWT is not a silver bullet. Always consider your application’s specific needs and weigh the trade-offs. Combine JWT with other security best practices like HTTPS, short expiry, refresh tokens, and secure storage.
Summary
- JWT = JSON-based token for secure information exchange.
- Great for stateless authentication in APIs and SPAs.
- Structure: Header, Payload, Signature.
- Easy to implement in Node.js, Python, Java, and many other stacks.
- Store securely, validate carefully, and always use HTTPS.