Introduction
Security is a critical aspect of web development, especially when handling user authentication and sensitive data. In this chapter, we will cover authentication methods, password hashing, JWT-based authentication, and security best practices for Node.js applications.
1. Authentication Methods
There are multiple ways to authenticate users in a Node.js application:
- Session-based authentication (using cookies and sessions)
- Token-based authentication (using JSON Web Tokens - JWT)
- OAuth authentication (e.g., Google, Facebook, GitHub login)
We will focus on JWT-based authentication, which is widely used for modern web applications and APIs.
2. Hashing Passwords with bcrypt
Storing passwords in plain text is a major security risk. Instead, passwords should be hashed before storage.
Install bcrypt
npm install bcrypt
Example: Hashing a Password
const bcrypt = require("bcrypt");
const saltRounds = 10;
const hashPassword = async (password) => {
const hashedPassword = await bcrypt.hash(password, saltRounds);
console.log("Hashed Password:", hashedPassword);
};
hashPassword("mySecurePassword");
Example: Verifying a Password
const verifyPassword = async (password, hash) => {
const match = await bcrypt.compare(password, hash);
console.log("Password Match:", match);
};
3. Implementing JWT Authentication
JWT (JSON Web Tokens) is a popular method for secure authentication in web applications.
Install jsonwebtoken
npm install jsonwebtoken
Example: Generating a JWT Token
const jwt = require("jsonwebtoken");
const secretKey = "supersecretkey";
const generateToken = (user) => {
return jwt.sign({ id: user.id, username: user.username }, secretKey, { expiresIn: "1h" });
};
const token = generateToken({ id: 1, username: "Alice" });
console.log("JWT Token:", token);
Example: Verifying a JWT Token
const verifyToken = (token) => {
try {
const decoded = jwt.verify(token, secretKey);
console.log("Token Decoded:", decoded);
} catch (err) {
console.log("Invalid Token");
}
};
4. Implementing Authentication in Express.js
Install Required Packages
npm install express jsonwebtoken bcrypt
Example: Setting Up Authentication Routes
const express = require("express");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const app = express();
app.use(express.json());
const users = [];
const secretKey = "supersecretkey";
// Register Route
app.post("/register", async (req, res) => {
const hashedPassword = await bcrypt.hash(req.body.password, 10);
users.push({ username: req.body.username, password: hashedPassword });
res.status(201).send("User Registered");
});
// Login Route
app.post("/login", async (req, res) => {
const user = users.find(u => u.username === req.body.username);
if (!user || !(await bcrypt.compare(req.body.password, user.password))) {
return res.status(403).send("Invalid credentials");
}
const token = jwt.sign({ username: user.username }, secretKey, { expiresIn: "1h" });
res.json({ token });
});
// Protected Route
app.get("/protected", (req, res) => {
const token = req.headers.authorization;
if (!token) return res.status(401).send("Access Denied");
try {
const verified = jwt.verify(token, secretKey);
res.send("Protected content for " + verified.username);
} catch {
res.status(403).send("Invalid token");
}
});
app.listen(3000, () => console.log("Server running on port 3000"));
5. Security Best Practices in Node.js
To improve security, follow these best practices:
- Use environment variables for secrets (
dotenv
package) - Limit API request rates to prevent abuse (use
express-rate-limit
) - Validate user input to prevent SQL Injection & XSS (use
express-validator
) - Use HTTPS for encrypted communication
- Enable CORS properly when working with APIs
Example: Using dotenv for Environment Variables
npm install dotenv
require("dotenv").config();
const secretKey = process.env.SECRET_KEY;
Example: Rate Limiting API Requests
npm install express-rate-limit
const rateLimit = require("express-rate-limit");
const limiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100 });
app.use(limiter);
š Exercises
- 1. Create a registration and login system using bcrypt and JWT.
- 2. Implement middleware to protect a route using JWT authentication.
- 3. Store sensitive values using environment variables and
dotenv
. - 4. Implement rate-limiting to prevent brute-force attacks.
- 5. Improve your authentication system by adding password reset functionality.
Conclusion
Authentication and security are essential in any web application. This chapter covered password hashing, JWT authentication, secure routes, and best practices to protect your Node.js applications.