javascript 53 lines · 9 steps

Role-based permissions middleware in Express

A middleware factory turns a static role-to-permission map into per-route access control.

Explained by highlit
1const ROLE_PERMISSIONS = {
2 admin: ['users:read', 'users:write', 'billing:read', 'billing:write'],
3 manager: ['users:read', 'billing:read'],
4 member: ['users:read'],
5};
6 
7function hasPermission(role, permission) {
8 const granted = ROLE_PERMISSIONS[role] || [];
9 return granted.includes(permission);
10}
11 
12function requirePermission(...required) {
13 return (req, res, next) => {
14 if (!req.user) {
15 return res.status(401).json({ error: 'Authentication required' });
16 }
17 
18 const missing = required.filter(
19 (permission) => !hasPermission(req.user.role, permission)
20 );
21 
22 if (missing.length > 0) {
23 return res.status(403).json({
24 error: 'Insufficient permissions',
25 required: missing,
26 });
27 }
28 
29 next();
30 };
31}
32 
33const router = require('express').Router();
34 
35router.get(
36 '/users',
37 requirePermission('users:read'),
38 usersController.list
39);
40 
41router.post(
42 '/users',
43 requirePermission('users:write'),
44 usersController.create
45);
46 
47router.patch(
48 '/billing/:id',
49 requirePermission('billing:read', 'billing:write'),
50 billingController.update
51);
52 
53module.exports = { router, requirePermission };
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A middleware factory lets you configure per-route behavior by returning a closure over its arguments.
  2. 2Centralizing permissions in one map keeps authorization logic declarative and easy to audit.
  3. 3Distinguishing 401 from 403 correctly separates 'not logged in' from 'not allowed'.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Role-based permissions middleware in Express — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code