JWT
What is a JWT?
A JSON Web Token (JWT) is a secure way of transmitting information between parties as a JSON object. It's widely used in authentication systems, where servers need a way to verify the identity of clients (e.g., browsers or mobile apps) without storing session state on the server.
Each JWT contains three base64-encoded components:
- Header: Specifies the signing algorithm (e.g., HS256) and token type (usually “JWT”).
- Payload: Holds user-related data (also called claims). This can include things like
userId
,email
,roles
, etc. - Signature: Created by hashing the encoded header and payload with a secret key using the algorithm specified in the header.
Example:
Header: {"alg": "HS256", "typ": "JWT"}
Payload: {"userId": 123, "email": "joe@example.com"}
Signature: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
JWTs are not encrypted by default — they are merely encoded. So never store sensitive data like passwords in them.
Why Use JWT?
JWTs are popular in modern web and mobile development for several reasons:
1. Stateless Authentication
JWT enables stateless authentication, meaning the server doesn’t need to keep a session or track user state. This is ideal for:
- RESTful APIs
- Microservices
- Serverless environments
No need for database lookups on every request — user identity is self-contained in the token.
2. Scalability
JWTs allow your backend to scale horizontally with ease. There’s no need to sync session data between instances, making it great for distributed or cloud-native systems.
3. Cross-domain and Cross-platform Support
JWTs are platform-agnostic and can be used across:
- Browsers
- Mobile apps
- IoT devices
- Microservices
They're transmitted via standard HTTP headers or URLs and don’t rely on cookies.
4. Fine-grained Control via Claims
JWTs support custom claims, enabling:
- Role-based access control (RBAC)
- Scoped permissions
- Expiration management
5. Widely Supported
JWT libraries are available for most modern languages and frameworks, making integration easy.
Alternatives to JWT
1. Session-Based Authentication
How it works:
- Server stores user session in memory or database.
- Client gets a session ID, usually via a cookie.
- Each request includes the session ID.
Pros:
- Simple and secure (especially with cookies + HTTPS)
- Easy session revocation
Cons:
- Requires stateful server
- Difficult to scale horizontally
Best for: Traditional server-rendered web apps.
2. OAuth 2.0 + Access Tokens
How it works:
- Involves authorization servers and resource servers.
- Often uses JWT as access tokens.
Pros:
- Secure and standardized
- Supports scopes, delegated access
Cons:
- More complex to implement
- Overkill for small-scale apps
Best for: APIs needing 3rd-party access (e.g., “Login with Google”).
3. API Keys
How it works:
- Static API key is sent with requests.
Pros:
- Simple to implement
- Good for service-to-service authentication
Cons:
- No user identity
- No expiration or standard revocation
Best for: Internal APIs or machine-to-machine auth.
4. Paseto (Platform-Agnostic Security Tokens)
How it works:
- Secure alternative to JWT with built-in encryption options.
Pros:
- Safer by design
- Easier to audit
- No insecure algorithms
Cons:
- Less widely adopted
- Fewer libraries
Best for: Apps wanting JWT-style features with better defaults.
Setting Up JWT in a Node.js + Express + TypeScript Project
Setup & Dependencies
npm init -y
npm install express jsonwebtoken dotenv
npm install --save-dev typescript ts-node @types/express @types/jsonwebtoken
npx tsc --init
Project Structure
jwt-auth-demo/
├── src/
│ ├── index.ts
│ ├── auth.ts
│ └── types.ts
├── .env
├── tsconfig.json
.env
JWT_SECRET=supersecretkey123
Example TypeScript Code
types.ts
export interface UserPayload {
id: number;
email: string;
}
auth.ts
import jwt from 'jsonwebtoken';
import { Request, Response, NextFunction } from 'express';
import { UserPayload } from './types';
const SECRET = process.env.JWT_SECRET || 'fallback_secret';
export function generateToken(user: UserPayload): string {
return jwt.sign(user, SECRET, { expiresIn: '1h' });
}
export function authenticateToken(req: Request, res: Response, next: NextFunction) {
const authHeader = req.headers['authorization'];
const token = authHeader?.split(' ')[1];
if (!token) {
return res.status(401).json({ message: 'Missing token' });
}
jwt.verify(token, SECRET, (err, user) => {
if (err) return res.status(403).json({ message: 'Invalid or expired token' });
req.user = user as UserPayload;
next();
});
}
index.ts
import express, { Request, Response } from 'express';
import dotenv from 'dotenv';
import { generateToken, authenticateToken } from './auth';
import { UserPayload } from './types';
dotenv.config();
const app = express();
app.use(express.json());
const PORT = 3000;
const dummyUser: UserPayload = { id: 1, email: 'test@example.com' };
app.post('/login', (req: Request, res: Response) => {
const token = generateToken(dummyUser);
res.json({ token });
});
app.get('/protected', authenticateToken, (req: Request, res: Response) => {
res.json({
message: 'Access granted to protected route',
user: req.user
});
});
app.listen(PORT, () => console.log(`Server running at http://localhost:${PORT}`));
How to Test JWT Auth
1. Get a Token
Use Postman or curl
:
curl -X POST http://localhost:3000/login
You’ll receive a JSON response:
{ "token": "eyJhbGciOi..." }
2. Access Protected Route
curl -H "Authorization: Bearer <your_token>" http://localhost:3000/protected
You should see a success message and user data if the token is valid. However, you can also take the JSON response and paste it to the JWT debugger to see and check the result.
Good Practices
- Use HTTPS in production to prevent token interception.
- Set reasonable expiration times (
expiresIn
) and implement token refresh strategies. - Blacklist tokens on logout (especially for critical apps).
- Avoid storing JWTs in
localStorage
if possible (considerHttpOnly
cookies for XSS protection).
Summary
JWTs provide a fast, scalable, and secure way to handle authentication in modern applications. With this approach:
- Users authenticate once and receive a token.
- The token is used on every request to prove their identity.
- The server doesn’t have to store session state — it's all in the token.
JWTs are a key piece of modern REST APIs, microservices, and serverless architectures.