Previous articles:
How to Make API using NodeJS and Express
Enhancing Our Node.js API: Hot Reloading with Nodemon, Bundling with Webpack & Using ES6
1. JWT Overview
JSON Web Tokens (JWT) offer a compact and self-contained way to securely transmit information between parties as a JSON object. In authentication, when the user logs in using their credentials, a server-side application provides a signed token to the client. The client then uses this token to access protected routes or resources.
2. Setting up JWT Authentication
Step 1: Install the necessary packages:
npm install jsonwebtoken bcryptjs
jsonwebtoken
- For generating and verifying JWTs.bcryptjs
- For hashing and checking user passwords.
Step 2: Set up a User Model with Password Hashing (models/user.js):
If not already set, add a password field to the user schema. Then, implement password hashing using bcrypt:
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const userSchema = new mongoose.Schema({
// ... other fields ...
password: {
type: String,
required: true,
},
});
// Hash password before saving
userSchema.pre('save', async function(next) {
if (this.isModified('password')) {
this.password = await bcrypt.hash(this.password, 10);
}
next();
});
module.exports = mongoose.model('User', userSchema);
Step 3: Implement Signup and Login Routes:
Signup:
const bcrypt = require('bcryptjs'); const User = require('../models/user'); exports.signup = async (req, res) => { const user = new User(req.body); await user.save(); res.status(201).json({ message: "User registered successfully!" }); };
Login:
const jwt = require('jsonwebtoken'); exports.login = async (req, res) => { const { email, password } = req.body; const user = await User.findOne({ email }); if (!user || !await bcrypt.compare(password, user.password)) { return res.status(401).json({ error: "Invalid email or password" }); } const token = jwt.sign({ userId: user.id }, 'YOUR_SECRET_KEY', { expiresIn: '1h' }); res.json({ token }); };
Remember to replace 'YOUR_SECRET_KEY'
with your actual secret key.
3. Protecting Routes
To protect routes, we create a middleware that verifies the JWT:
Middleware (middlewares/auth.js):
const jwt = require('jsonwebtoken');
exports.verifyToken = (req, res, next) => {
const token = req.headers['authorization'];
if (!token) {
return res.status(403).json({ error: 'No token provided' });
}
jwt.verify(token, 'YOUR_SECRET_KEY', (err, decoded) => {
if (err) {
return res.status(401).json({ error: 'Unauthorized' });
}
req.userId = decoded.userId;
next();
});
};
Now, apply this middleware to any route you want to protect. For instance, to protect a route that fetches user data:
const express = require('express');
const { verifyToken } = require('../middlewares/auth');
const router = express.Router();
router.get('/protectedRoute', verifyToken, (req, res) => {
// Your protected route logic here
});
module.exports = router;
With the setup above, only authenticated users with valid JWTs can access the protected routes. Remember to handle security considerations, such as storing JWTs securely in frontend applications, using HTTPS, and frequently rotating your secret keys.
With this, your Node.js and Express API now has a robust authentication mechanism using JWTs and can protect routes from unauthorized access.