- Express.js Basics
- Express.js HOME
- Express.js Introduction
- Express.js Installation
- Express.js Basic App
- Express.js Routing
- Basics Routing
- Route Parameters
- Handling Query Strings
- Router Middleware
- Middleware
- What is Middleware?
- Application-Level Middleware
- Router-Level Middleware
- Built-In Middleware
- Error-Handling Middleware
- Third-Party Middleware
- Express.js HTTP
- Handling GET Requests
- Handling POST Requests
- Handling PUT Requests
- Handling DELETE Requests
- Templating Engines
- Using Templating Engines
- Setting Up EJS
- Setting Up Handlebars
- Setting Up Pug
- Request/Response
- Request Object
- Response Object
- Handling JSON Data
- Handling Form Data
- Static Files
- Serving Static Files
- Setting Up Static Folders
- Managing Assets
- Express.js Advanced
- Middleware Stack
- CORS in Express.js
- JWT Authentication
- Session Handling
- File Uploads
- Error Handling
- Databases
- Express.js with MongoDB
- MongoDB CRUD Operations
- Express.js with MySQL
- MySQL CRUD Operations
- Deployment
- Deploying Express.js Apps to Heroku
- Deploying Express.js Apps to AWS
- Deploying Express.js Apps to Vercel
Express.js JWT Authentication
JSON Web Token (JWT) is the industry standard for securely transmitting information between a client and a server. In the world of modern web development, particularly with Express.js, JWTs have replaced traditional session-based authentication for most APIs. Think of a JWT as a digital "ID card" that the server issues to a user; for every subsequent request, the user presents this card to prove who they are without the server needing to look up their session in a database.
Key Features of JWT Authentication
- Token-Based Authentication: Instead of sending cookies back and forth, the client sends a cryptographically signed token in the HTTP headers.
- Stateless and Scalable: The server doesn't need to store session data in memory or a database. This makes it incredibly easy to scale your application across multiple servers.
- Compact and Secure: JWTs are URL-safe and can be passed through headers or even query parameters. Because they are digitally signed, the server can trust the data inside them hasn't been tampered with.
- Granular Control: You can embed specific user permissions (roles) and expiration times directly into the token payload.
Steps to Implement JWT Authentication
Install Required Packages
To get started, we need two core libraries: jsonwebtoken to handle the creation and verification of tokens, and bcryptjs to handle secure password hashing.
npm install jsonwebtoken bcryptjs
bcryptjs instead of the native bcrypt library if you want to avoid issues with C++ build dependencies during deployment, especially on environments like Docker or Heroku.
Create a Token
Once a user provides a valid username and password, you generate a token. This token is then sent back to the client, which usually stores it in localStorage or an HttpOnly cookie.
Example:
const jwt = require('jsonwebtoken');
// The payload contains the user data you want to store
const payload = { id: user.id, role: 'admin' };
const secret = process.env.JWT_SECRET; // Always use an environment variable
const token = jwt.sign(payload, secret, { expiresIn: '2h' });
res.json({ token });
expiresIn value (e.g., '1h' or '24h').
Verify a Token
To protect a route, you create a middleware function. This function intercepts the request, looks for the token in the Authorization header, and validates it.
Example:
const authenticateToken = (req, res, next) => {
// Expected format: "Bearer <token>"
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.status(401).send('Access Denied: No Token Provided');
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.status(403).send('Invalid or Expired Token');
// Attach the user data to the request object
req.user = user;
next();
});
};
// Apply the middleware to a route
app.get('/api/dashboard', authenticateToken, (req, res) => {
res.send(`Welcome User ID: ${req.user.id}`);
});
Hash Passwords with Bcrypt
Never store plain-text passwords in your database. Use bcryptjs to turn a password into a "hash" that cannot be reversed.
Example:
const bcrypt = require('bcryptjs');
// Higher salt rounds = more secure, but slower processing
const saltRounds = 10;
const hashedPassword = bcrypt.hashSync(userPassword, saltRounds);
Compare Passwords
During login, you compare the plain-text password provided by the user with the hashed version stored in your database.
Example:
const isMatch = bcrypt.compareSync(submittedPassword, storedHashedPassword);
if (!isMatch) return res.status(400).send('Invalid Credentials');
Complete Example of JWT Authentication
Here is a consolidated example showing how these pieces fit together in a real Express application.
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const app = express();
app.use(express.json());
// In a real app, this would be a database like MongoDB or PostgreSQL
const users = [];
const JWT_SECRET = 'super_secret_key_123';
// 1. Register: Hash password and save user
app.post('/register', (req, res) => {
const { username, password } = req.body;
const hashedPassword = bcrypt.hashSync(password, 10);
users.push({ id: users.length + 1, username, password: hashedPassword });
res.status(201).send('User registered successfully');
});
// 2. Login: Verify password and issue JWT
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username);
if (!user || !bcrypt.compareSync(password, user.password)) {
return res.status(401).send('Invalid username or password');
}
const token = jwt.sign({ id: user.id, username: user.username }, JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
});
// 3. Protected Route: Only accessible with a valid token
app.get('/profile', (req, res) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.status(401).send('Not authorized');
jwt.verify(token, JWT_SECRET, (err, decoded) => {
if (err) return res.status(403).send('Token is no longer valid');
res.json({ message: `Welcome ${decoded.username}`, userId: decoded.id });
});
});
app.listen(3000, () => {
console.log('Auth Server running on http://localhost:3000');
});
Summary
JWT Authentication is a powerful tool for building secure Express.js applications. By shifting the responsibility of state from the server to the client, you create an architecture that is lightweight and highly scalable. Remember to always use environment variables for your secrets, hash passwords before they touch your database, and keep your token expiration times short to minimize security risks.