5.1 KiB

Backend Middleware

Middleware components provide cross-cutting concerns for authentication, authorization, validation, rate limiting, and error handling across all API endpoints.

Middleware Architecture

Express middleware functions are composed in the request pipeline to:

  • Authenticate users via JWT tokens
  • Authorize access based on user roles
  • Validate request bodies against Zod schemas
  • Apply rate limits to prevent abuse
  • Handle errors consistently
  • Log requests and responses

Core Middleware

Authentication

authenticate (middleware/auth.ts)

  • Verifies JWT access tokens from Authorization header
  • Attaches req.user object with user ID, email, and role
  • Returns 401 Unauthorized for missing/invalid tokens
  • Supports both admin and public route protection
router.get('/profile', authenticate, async (req, res) => {
  // req.user is guaranteed to exist
  const userId = req.user.id;
});

Authorization

requireRole (middleware/auth.ts)

  • Checks if authenticated user has one of the required roles
  • Returns 403 Forbidden if role check fails
  • Supports multiple roles (OR logic)
router.post(
  '/campaigns',
  authenticate,
  requireRole('SUPER_ADMIN', 'INFLUENCE_ADMIN'),
  async (req, res) => {
    // Only admins can create campaigns
  }
);

requireNonTemp (middleware/auth.ts)

  • Blocks TEMP users from accessing endpoints
  • Used for routes that require full user accounts
  • TEMP users are created during shift signups
router.post(
  '/shifts/:id/signup',
  authenticate,
  requireNonTemp,
  async (req, res) => {
    // TEMP users cannot sign up for shifts
  }
);

Validation

validate (middleware/validate.ts)

  • Validates request body against Zod schemas
  • Returns 400 Bad Request with sanitized error messages
  • Supports nested validation and type coercion
  • Prevents injection attacks by sanitizing output
import { validate } from '../middleware/validate';
import { createCampaignSchema } from './campaigns.schemas';

router.post(
  '/campaigns',
  authenticate,
  validate(createCampaignSchema),
  async (req, res) => {
    // req.body is type-safe and validated
  }
);

Rate Limiting

rateLimit (middleware/rate-limit.ts)

  • Uses Redis for distributed rate limiting
  • Configurable window size and max requests
  • Returns 429 Too Many Requests when limit exceeded
  • Per-IP tracking with X-RateLimit headers

Common Configurations:

// Auth endpoints: 10 requests per minute
rateLimitRedis({
  windowMs: 60 * 1000,
  max: 10,
  standardHeaders: true,
  legacyHeaders: false,
  keyPrefix: 'rl:auth:',
})

// Canvass visits: 30 requests per minute
rateLimitRedis({
  windowMs: 60 * 1000,
  max: 30,
  keyPrefix: 'rl:canvass-visit:',
})

Error Handling

errorHandler (middleware/error-handler.ts)

  • Catches all unhandled errors in routes
  • Formats errors consistently as JSON
  • Logs errors with Winston
  • Sanitizes error messages in production
  • Returns appropriate HTTP status codes

Request Logging

requestLogger (middleware/logger.ts)

  • Logs all incoming requests with Morgan
  • Tracks response times
  • Formats logs with Winston
  • Separates error logs from access logs

Middleware Composition

Middleware is applied in order and can be composed:

// Global middleware (applies to all routes)
app.use(helmet());
app.use(cors());
app.use(requestLogger);

// Route-specific middleware
router.post(
  '/api/campaigns',
  rateLimit({ max: 100 }),
  authenticate,
  requireRole('SUPER_ADMIN', 'INFLUENCE_ADMIN'),
  validate(createCampaignSchema),
  campaignController.create
);

// Global error handler (last middleware)
app.use(errorHandler);

Security Features

Password Policy

  • Enforced at schema validation level
  • 12+ characters required
  • Must include uppercase, lowercase, and digit
  • Validated in auth.schemas.ts

User Enumeration Prevention

  • Returns 401 instead of 404 for missing users
  • Consistent response times for invalid credentials
  • No detailed error messages about which field failed

Refresh Token Rotation

  • Atomic transaction to prevent race conditions
  • Invalidates old refresh token on rotation
  • Tracks token family for security
  • Automatic cleanup of expired tokens

Redis Authentication

  • All Redis connections require password
  • Configured via REDIS_PASSWORD environment variable
  • Prevents unauthorized cache/queue access

Middleware Chain

Typical middleware chain for protected routes:

  1. CORS - Handle cross-origin requests
  2. Helmet - Security headers
  3. Request Logger - Log incoming request
  4. Body Parser - Parse JSON body
  5. Rate Limit - Check rate limits
  6. Authenticate - Verify JWT token
  7. Authorize - Check user role
  8. Validate - Validate request schema
  9. Route Handler - Execute business logic
  10. Error Handler - Catch and format errors