SyntaxStudy
Sign Up
Express.js Role-Based Access Control (RBAC)
Express.js Beginner 1 min read

Role-Based Access Control (RBAC)

Role-based access control restricts API endpoints based on the authenticated user's role. After JWT verification places the user's role on `req.user`, a simple `authorize(roles)` middleware factory checks whether the user's role is in the allowed list and returns 403 Forbidden if not. For fine-grained permissions beyond simple roles, use a permission-based model where each user has a set of permission strings like `posts:write` or `users:delete`. The token carries these permissions and the middleware checks for specific ones. This avoids the combinatorial explosion of roles when you need many combinations of capabilities. Attribute-based access control (ABAC) goes further, allowing policies that consider resource attributes (e.g., a user can only edit their own posts). Implement this by loading the target resource and comparing its owner field against `req.user.sub`. Keep this logic in middleware or a dedicated policy layer, not scattered across route handlers.
Example
const express = require('express');
const jwt     = require('jsonwebtoken');
const router  = express.Router();

const SECRET = process.env.JWT_SECRET || 'secret';

// Auth middleware — attaches decoded payload to req.user
function authenticate(req, res, next) {
    const [, token] = (req.headers.authorization || '').split(' ');
    try {
        req.user = jwt.verify(token, SECRET);
        next();
    } catch {
        res.status(401).json({ error: 'Unauthorized' });
    }
}

// RBAC middleware factory
function authorize(...roles) {
    return (req, res, next) => {
        if (!roles.includes(req.user?.role)) {
            return res.status(403).json({ error: 'Forbidden' });
        }
        next();
    };
}

// Permission-based guard
function can(permission) {
    return (req, res, next) => {
        const perms = req.user?.permissions || [];
        if (!perms.includes(permission)) {
            return res.status(403).json({ error: `Missing permission: ${permission}` });
        }
        next();
    };
}

// Routes
router.get('/admin/dashboard',
    authenticate, authorize('admin'), (req, res) => {
    res.json({ secret: 'admin data' });
});

router.delete('/posts/:id',
    authenticate, can('posts:delete'), (req, res) => {
    res.status(204).end();
});

module.exports = router;