Script Valley
JWT & Session Auth: Deep Dive
Role-Based Access ControlLesson 5.2

implementing permission middleware in Express

permission check middleware, hasPermission helper, role-permission mapping, middleware factory pattern, combining auth and authz middleware, error response consistency

Permission Middleware in Express

Permission Middleware Chain

Build permission checks as composable middleware that reads from a role-permission map.

const ROLE_PERMISSIONS = {
  viewer: new Set(['post:read']),
  editor: new Set(['post:read', 'post:create', 'post:update:own']),
  admin: new Set(['post:read', 'post:create', 'post:update:any', 'post:delete:any', 'user:ban'])
};

function hasPermission(role, permission) {
  return ROLE_PERMISSIONS[role]?.has(permission) ?? false;
}

// Middleware factory
function requirePermission(permission) {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Authentication required' });
    }
    if (!hasPermission(req.user.role, permission)) {
      return res.status(403).json({ error: 'Insufficient permissions' });
    }
    next();
  };
}

// Usage
app.delete('/posts/:id',
  requireAuth,
  requirePermission('post:delete:any'),
  deletePostHandler
);

Centralize your permission map. Never scatter role checks inline through route handlers — a change to roles requires hunting through the codebase. One source of truth means one place to update.

Use Set for permission collections — O(1) lookup vs O(n) for arrays, which matters when checking permissions on every request.

Up next

resource ownership checks: can this user edit this post

Sign in to track progress