Script Valley
Web Security Fundamentals for Developers
Access Control and AuthorizationLesson 5.5

Implementing middleware-based authorization in Express

authorization middleware chain, authentication vs authorization, centralized vs route-level enforcement, route guard pattern, middleware composition, error handling in auth middleware

Authorization Middleware in Express

Express authorization middleware chain diagram

Authentication confirms who you are. Authorization confirms what you're allowed to do. These are separate concerns and should be separate middleware functions composed in sequence.

Middleware Composition Pattern

// 1. Authentication middleware — verifies token, attaches user to req
function authenticate(req, res, next) {
  const token = req.cookies.token;
  if (!token) return res.status(401).json({ error: 'Authentication required' });

  try {
    req.user = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] });
    next();
  } catch {
    res.status(401).json({ error: 'Invalid token' });
  }
}

// 2. Authorization middleware factory — checks role
function requireRole(...roles) {
  return (req, res, next) => {
    if (!roles.includes(req.user.role)) {
      return res.status(403).json({ error: 'Forbidden' });
    }
    next();
  };
}

// 3. Ownership middleware — checks resource belongs to user
function requireOwnership(Model) {
  return async (req, res, next) => {
    const resource = await Model.findById(req.params.id);
    if (!resource) return res.status(404).json({ error: 'Not found' });
    if (resource.userId.toString() !== req.user.id) {
      return res.status(403).json({ error: 'Forbidden' });
    }
    req.resource = resource; // Attach for route handler
    next();
  };
}

// Compose as needed
app.delete('/posts/:id', authenticate, requireRole('admin', 'editor'), deletePost);
app.get('/invoices/:id', authenticate, requireOwnership(Invoice), getInvoice);

Always Authenticate Before Authorizing

Authorization middleware depends on req.user being set. Ensure authenticate always runs before any authorization check. Use router-level middleware for groups of routes to avoid forgetting it on individual routes.

Implementing middleware-based authorization in Express — Access Control and Authorization — Web Security Fundamentals for Developers — Script Valley — Script Valley