refactor(backend): extract authMiddleware and introduce createApp factory (phase 2) (#732)

Phase 2 of the index.ts refactor. Pulls the auth middleware and session
cookie issuers into their own module, and introduces the app.ts factory
that owns the first nine steps of the canonical middleware pipeline.

New modules:
- middleware/auth.ts: authMiddleware, issueSessionCookie,
  issueMfaPendingCookie, clearMfaPendingCookie
- app.ts: createApp() factory installing trust proxy, helmet, cors,
  compression, cookieParser, rate limiters, conditionalJsonParser, and
  nodeContextMiddleware. A header comment documents all 16 canonical
  middleware steps and where each currently lives.

Changes:
- middleware/authGate.ts: createAuthGate factory removed; authGate now
  imports authMiddleware directly (the factory existed only to avoid a
  circular import while authMiddleware lived in index.ts).
- services/DatabaseService.ts: added API_TOKEN_SCOPE_TO_ROLE map so the
  auth middleware no longer inlines a stringly-typed record.
- index.ts drops ~260 lines; auth routes, authGate, auditLog,
  apiTokenScope, remaining routes, static serving, and the error handler
  continue to be registered there until their respective phases.
- vitest.config.ts bumps testTimeout to 30s and hookTimeout to 45s so
  fork-pool workers have enough headroom to ts-node-transform the
  growing module graph under CPU contention (64 workers each import the
  full Express stack in beforeAll).

Code review fixes: use getErrorMessage() util in the auth catch block
instead of inline cast; promote the scope-to-role map to a typed
module-level constant.
This commit is contained in:
Anso
2026-04-23 19:02:13 -04:00
committed by GitHub
parent 856a260a11
commit ca5a930c68
6 changed files with 368 additions and 297 deletions
+12 -17
View File
@@ -3,26 +3,21 @@ import { DatabaseService } from '../services/DatabaseService';
import { isDebugEnabled } from '../utils/debug';
import { getAuditSummary } from '../utils/audit-summaries';
import { WEBHOOK_TRIGGER_RE } from '../helpers/routePatterns';
import { authMiddleware } from './auth';
/**
* Build the `/api/*` auth gate. Mounted at `/api`, so paths it sees are
* already stripped of the `/api` prefix. Exempts `/auth/*` (setup, login,
* SSO: handled by their own routes) and webhook triggers (authenticated
* via HMAC, not session).
*
* Takes `authMiddleware` as a dependency instead of importing it so this
* file does not pin the monolith's auth lifecycle. Phase 2 extracts
* `authMiddleware` into its own module.
* `/api/*` auth gate. Mounted at `/api`, so paths it sees are already
* stripped of the `/api` prefix. Exempts `/auth/*` (setup, login, SSO:
* handled by their own routes) and webhook triggers (authenticated via
* HMAC, not session).
*/
export function createAuthGate(authMiddleware: RequestHandler): RequestHandler {
return (req: Request, res: Response, next: NextFunction): void => {
if (req.path.startsWith('/auth/') || WEBHOOK_TRIGGER_RE.test(req.path)) {
next();
return;
}
authMiddleware(req, res, next);
};
}
export const authGate: RequestHandler = (req: Request, res: Response, next: NextFunction): void => {
if (req.path.startsWith('/auth/') || WEBHOOK_TRIGGER_RE.test(req.path)) {
next();
return;
}
authMiddleware(req, res, next);
};
/**
* Audit-logging middleware. Records every mutating `/api/*` action for