JWT Authentication and Authorization in Nodejs: Implementing authentication and authorization in a Nodejs application is crucial for securing your web app.
In this blog post, we’ll walk through the process of setting up user authentication using JSON Web Tokens (JWT) and implementing role-based authorization.
Easy Steps to Implement
Before we start, make sure you have Node.js and npm installed. You can check this by running the following commands:
node -v
npm -v
Step 1: Initialize Your Project
First, create a new directory for your project and initialize it with npm:
mkdir auth-demo
cd auth-demo
npm init -y
Read Also : Implementing Code Coverage in Node js: A Comprehensive Guide Learn in 10 min
Step 2: Install Dependencies
We’ll need several npm packages for this project:
express
for the web server.bcryptjs
for hashing passwords.jsonwebtoken
for creating and verifying JWTs.mongoose
for interacting with MongoDB.dotenv
for managing environment variables.
Install these packages by running:
npm install express bcryptjs jsonwebtoken mongoose dotenv
Step 3: Setup Your Project Structure
Create the following files and directories to organize your project:
auth-demo/
│
├── config/
│ └── db.js
├── controllers/
│ └── authController.js
├── middleware/
│ └── authMiddleware.js
├── models/
│ └── User.js
├── routes/
│ └── authRoutes.js
├── .env
├── app.js
└── package.json
Step 4: Configure Environment Variables
Create a .env
file to store your environment variables:
MONGO_URI=your_mongodb_connection_string
JWT_SECRET=your_jwt_secret
Read Also: Securing Your Node.js API with Encryption and Sending Dynamic IV to Client : AES-CBC
Step 5: Setup MongoDB Connection
In config/db.js
, setup your MongoDB connection:
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log('MongoDB connected');
} catch (err) {
console.error(err.message);
process.exit(1);
}
};
module.exports = connectDB;
Step 6: Define the User Model
In models/User.js
, create a User schema:
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const UserSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
role: { type: String, required: true, enum: ['user', 'admin'], default: 'user' },
});
UserSchema.pre('save', async function (next) {
if (!this.isModified('password')) {
return next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
});
UserSchema.methods.matchPassword = async function (password) {
return await bcrypt.compare(password, this.password);
};
module.exports = mongoose.model('User', UserSchema);
Step 7: Create Authentication Controller
In controllers/authController.js
, define your authentication logic:
const User = require('../models/User');
const jwt = require('jsonwebtoken');
const register = async (req, res) => {
const { username, password } = req.body;
try {
let user = await User.findOne({ username });
if (user) {
return res.status(400).json({ msg: 'User already exists' });
}
user = new User({ username, password });
await user.save();
const payload = { user: { id: user.id, role: user.role } };
const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '1h' });
res.status(201).json({ token });
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
};
const login = async (req, res) => {
const { username, password } = req.body;
try {
const user = await User.findOne({ username });
if (!user) {
return res.status(400).json({ msg: 'Invalid credentials' });
}
const isMatch = await user.matchPassword(password);
if (!isMatch) {
return res.status(400).json({ msg: 'Invalid credentials' });
}
const payload = { user: { id: user.id, role: user.role } };
const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
};
module.exports = { register, login };
Read Also : Implementing Rate Limiting in Node APIs: Examples and Best Practices
Step 8: Create Authentication Middleware
In middleware/authMiddleware.js
, create middleware to protect routes:
const jwt = require('jsonwebtoken');
const auth = (req, res, next) => {
const token = req.header('x-auth-token');
if (!token) {
return res.status(401).json({ msg: 'No token, authorization denied' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded.user;
next();
} catch (err) {
res.status(401).json({ msg: 'Token is not valid' });
}
};
const authorize = (roles) => {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({ msg: 'Forbidden' });
}
next();
};
};
module.exports = { auth, authorize };
Step 9: Create Routes
In routes/authRoutes.js
, define your routes:
const express = require('express');
const router = express.Router();
const { register, login } = require('../controllers/authController');
const { auth, authorize } = require('../middleware/authMiddleware');
router.post('/register', register);
router.post('/login', login);
// Example of a protected route
router.get('/admin', auth, authorize(['admin']), (req, res) => {
res.send('Admin content');
});
module.exports = router;
Step 10: Setup Express Server
In app.js
, setup your Express server:
const express = require('express');
const connectDB = require('./config/db');
const authRoutes = require('./routes/authRoutes');
require('dotenv').config();
const app = express();
// Connect to database
connectDB();
// Middleware
app.use(express.json());
// Routes
app.use('/api/auth', authRoutes);
// Start server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Step 11: Test Your API
Start your server:
node app.js
Use a tool like Postman to test your API endpoints:
- Register a new user: POST
/api/auth/register
withusername
andpassword
in the body. - Login: POST
/api/auth/login
withusername
andpassword
in the body. - Access a protected route: GET
/api/auth/admin
with thex-auth-token
header containing the JWT without Bearer.
Conclusion : JWT Authentication and Authorization in Nodejs
In this tutorial, we implemented a basic JWT authentication and authorization in Nodejs. This setup provides a solid foundation for securing your web application.
We can expand on this by adding more features such as password reset, email verification, and more detailed role-based access controls.
Hey Tech Enthusiasts!
I’m Avinash, a passionate tech blogger with over 13+ years of experience in the trenches of software engineering.
You could say I’ve worn many hats in my journey – from full-stack developer crafting beautiful and functional applications to Solution Architect, designing the architecture for complex systems.
Over the years, I’ve delved into a vast arsenal of languages and tools, including the Generative AI (LLMs, LLM-Proxy, Observability, Prompt Engineering), .NET family (.NET, .NET Core), PHP, Rust, Python, the JavaScript frameworks (Angular, React, Node.js), and databases like MySQL, SQL Server, MongoDB.
As the cloud revolutionized our world, I’ve become well-versed in both Azure and GCP platforms, wielding Docker for containerization and CI/CD pipelines to streamline development workflows.
Here on my blog, I aim to share the knowledge I’ve accumulated and the lessons I’ve learned along the way. Whether you’re a seasoned developer or just starting your coding adventure, I want to provide you with insightful, practical articles that tackle real-world tech challenges.
Get ready to explore the latest advancements, delve into programming concepts, and discover efficient solutions to your development dilemmas. So, buckle up, tech enthusiasts – let’s embark on this exciting journey together!