Building a Role-Based Access Control System in Node.js and React
Role-Based Access Control (RBAC) is essential to secure your app by restricting what users can do based on their roles. Here’s how to implement RBAC using JWT, Express middleware, and React context.
1. Define Roles and Permissions
Start by defining roles and their permissions in your backend:
// roles.js
const roles = {
admin: ['read:any', 'write:any', 'delete:any'],
editor: ['read:any', 'write:any'],
user: ['read:own', 'write:own']
};
module.exports = roles;
2. Add Role to JWT Token on Login
Include user role info in the JWT token when logging in:
const token = jwt.sign(
{ id: user.id, role: user.role },
JWT_SECRET,
{ expiresIn: '1d' }
);
res.cookie('token', token, { httpOnly: true, secure: true, sameSite: 'Lax' });
res.status(200).json({ message: 'Logged in' });
3. Create Middleware to Check Permissions
Middleware verifies token and checks if user role has required permission:
// authMiddleware.js
const jwt = require('jsonwebtoken');
const roles = require('./roles');
function authorize(permission) {
return (req, res, next) => {
const token = req.cookies.token;
if (!token) return res.status(401).json({ error: 'Unauthorized' });
try {
const user = jwt.verify(token, JWT_SECRET);
req.user = user;
const userPermissions = roles[user.role] || [];
if (!userPermissions.includes(permission)) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
} catch {
res.status(401).json({ error: 'Invalid token' });
}
};
}
module.exports = authorize;
4. Protect Routes with Permission Checks
Use middleware with required permission for each route:
const express = require('express');
const authorize = require('./authMiddleware');
const router = express.Router();
router.get('/admin', authorize('read:any'), (req, res) => {
res.json({ message: 'Welcome admin!' });
});
router.post('/edit', authorize('write:any'), (req, res) => {
res.json({ message: 'Edit successful' });
});
5. Access Permissions in React
Use React Context or hooks to control UI based on user role and permissions.
Example: Show admin button conditionally:
{user.role === 'admin' && (
<button onClick={handleAdminAction}>Admin Panel</button>
)}
Final Thoughts
RBAC adds powerful control but requires thoughtful design. Keep roles and permissions centralized and verify them server-side for security.
