← back to Dw Boardroom Governance

src/api/middleware/auth.ts

41 lines

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

function safeEqual(a: string, b: string): boolean {
  const bufA = Buffer.from(a);
  const bufB = Buffer.from(b);
  // timingSafeEqual requires equal-length buffers; bail early on mismatch.
  if (bufA.length !== bufB.length) return false;
  return timingSafeEqual(bufA, bufB);
}

export function authMiddleware(req: Request, res: Response, next: NextFunction): void {
  const auth = req.headers.authorization;

  if (!auth || !auth.startsWith('Basic ')) {
    res.setHeader('WWW-Authenticate', 'Basic realm="DW Governance"');
    res.status(401).json({ error: 'Authentication required' });
    return;
  }

  const token = auth.split(' ')[1] || '';
  const decoded = Buffer.from(token, 'base64').toString();
  // Split on the first colon only — passwords may legitimately contain colons.
  const sep = decoded.indexOf(':');
  const user = sep >= 0 ? decoded.slice(0, sep) : '';
  const pass = sep >= 0 ? decoded.slice(sep + 1) : '';

  const expectedUser = process.env.AUTH_USER;
  const expectedPass = process.env.AUTH_PASS;

  // Fail closed if server credentials are not configured.
  if (expectedUser && expectedPass &&
      safeEqual(user, expectedUser) && safeEqual(pass, expectedPass)) {
    next();
    return;
  }

  res.setHeader('WWW-Authenticate', 'Basic realm="DW Governance"');
  res.status(401).json({ error: 'Invalid credentials' });
}