Learn how to secure Node.js APIs using JWT and role-based authorization. Protect routes, verify tokens, and manage user access effectively.
When building APIs for modern applications, especially those consumed by mobile or frontend frameworks like Ionic, ensuring secure access is critical. One of the most efficient ways to do this is by using JSON Web Tokens (JWT) along with role-based authorization.
In this guide, we’ll walk through how to:
- Authenticate users using JWT
- Protect API routes
- Control access based on user roles
Why Use JWT for Authentication?
JWT is a compact, self-contained way to securely transmit information between two parties. It’s widely used for stateless authentication in RESTful APIs. After a successful login, the server generates a signed token containing user details. This token is returned to the client and must be included with every future request to confirm the user's identity and permissions
Benefits of JWT:
- No session storage on the server
- Works well with mobile and SPA (single-page applications)
- Easily scalable and secure when implemented correctly
Setting Up the Project
First, let's create a basic Express API.
npm install express jsonwebtoken bcryptjs dotenv
Creating a Login Route
We’ll simulate a simple login system using hardcoded users for demonstration.
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
require('dotenv').config();
const app = express();
app.use(express.json());
// Simulated user database
const users = [
{ id: 1, username: 'admin', password: bcrypt.hashSync('admin123', 8), role: 'admin' },
{ id: 2, username: 'user', password: bcrypt.hashSync('user123', 8), role: 'user' }
];
// Login route
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = users.find((userEntry) => userEntry.username === username);
if (!user || !bcrypt.compareSync(password, user.password)) {
return res.status(401).json({ message: 'Invalid user credentials' });
}
const token = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({ token });
});
Middleware to Verify JWT
Create a middleware function to validate incoming tokens.
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader?.split(' ')[1];
if (!token) return res.status(401).json({ message: 'Token required' });
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.status(403).json({ message: 'user Token is invalid' });
req.user = user;
next();
});
}
Role-Based Access Control (RBAC)
Now, let's create middleware that allows access only to specific user roles.
function authorizeRoles(...allowedRoles) {
return (req, res, next) => {
if (!allowedRoles.includes(req.user.role)) {
return res.status(403).json({ message: 'user access denied: Insufficient permissions' });
}
next();
};
}
Protecting Routes
Use the middlewares on your API endpoints.
// Only accessible by admin
app.get('/admin', authenticateToken, authorizeRoles('admin'), (req, res) => {
res.send('Welcome, Admin!');
});
// Accessible by both admin and user
app.get('/dashboard', authenticateToken, authorizeRoles('admin', 'user'), (req, res) => {
res.send('Dashboard Access');
});
Testing the API
-
Make a POST request
/login
with username and password. -
Copy the token returned.
-
Make a GET request to
/admin
or/dashboard
With the token in theAuthorization
header:
Authorization: Bearer <token>
Summary
By using JWT-based authentication along with role-based authorization, your Node.js API becomes secure, scalable, and ready for production.
This pattern works seamlessly with mobile or web frontends, including hybrid apps built using Ionic + Angular or React.
0 Comments