← back to Jill Website

src/middleware/auth.ts

45 lines

import { Request, Response, NextFunction } from 'express';

/**
 * Simple authentication middleware for admin routes
 * In production, replace with proper session-based or JWT authentication
 */
export function requireAuth(req: Request, res: Response, next: NextFunction): void {
  const authHeader = req.headers.authorization;

  const adminPassword = process.env.ADMIN_PASSWORD;
  if (!adminPassword) {
    throw new Error('Missing required environment variable: ADMIN_PASSWORD');
  }

  if (!authHeader) {
    res.setHeader('WWW-Authenticate', 'Basic realm="Admin Area"');
    res.status(401).json({ error: 'Authentication required' });
    return;
  }

  // P1 fix 2026-05-04: malformed Authorization header (e.g., "Bearer", no space)
  // would crash with "Buffer.from(undefined, 'base64')" and leak a 500 stack trace.
  const parts = authHeader.split(' ');
  if (parts.length !== 2 || parts[0] !== 'Basic') {
    res.status(401).json({ error: 'Invalid authorization format' });
    return;
  }
  const auth = Buffer.from(parts[1], 'base64').toString().split(':');
  const username = auth[0] ?? '';
  const password = auth[1] ?? '';

  // P1 fix 2026-05-04: timing-safe compare to avoid leaking password length.
  const usernameOk = username === 'admin';
  const pwBuf = Buffer.from(password);
  const adminBuf = Buffer.from(adminPassword);
  const passwordOk = pwBuf.length === adminBuf.length
    && require('node:crypto').timingSafeEqual(pwBuf, adminBuf);

  if (usernameOk && passwordOk) {
    next();
  } else {
    res.status(403).json({ error: 'Invalid credentials' });
  }
}