refactor(backend): extract auth/MFA/SSO routers from index.ts (phase 4a-2) (#735)

Second slice of Phase 4. Pulls the three auth-family route groups out of
index.ts into focused routers. All handlers move verbatim; index.ts drops
~845 lines.

New route files:
- routes/auth.ts: /api/auth core (status, setup, login, password, logout,
  check, generate-node-token)
- routes/mfa.ts: /api/auth/login/mfa + full /api/auth/mfa/* surface
  (status, enroll start/confirm, disable, backup-codes/regenerate,
  sso-bypass)
- routes/sso.ts: /api/auth/sso/{providers,ldap,oidc/:provider/authorize,
  oidc/:provider/callback} + getSSOBaseUrl helper. Module-load calls
  SSOService.getInstance().seedFromEnv() so env-seeded providers are
  available on the first request.

Shared lifts:
- helpers/constants.ts: MFA_REPLAY_TTL_MS + MFA_REPLAY_PURGE_INTERVAL_MS
  (used by mfa.ts and the startup purge timer in index.ts) and
  BCRYPT_SALT_ROUNDS (shared between setup and password-change handlers).
- middleware/auth.ts: new reissueSessionAfterTokenBump(req, res, userId)
  helper collapses three copies of "bump → fetch user → re-sign cookie"
  across auth.ts (password change) and mfa.ts (enrol confirm, disable).

Code review fixes:
- File-local requireEnrolledMfaUser helper in mfa.ts eliminates four
  copies of "auth check + rejectApiTokenScope + load enrolled MFA" with
  near-identical shape.
- Applied BCRYPT_SALT_ROUNDS to auth.ts setup + password handlers.

Mount order in index.ts: authRouter / mfaRouter / ssoRouter sit before
authGate because login / setup / SSO-callback are public; handlers that
need auth use authMiddleware directly on the route.
This commit is contained in:
Anso
2026-04-23 20:43:32 -04:00
committed by GitHub
parent 50e64b058b
commit b329916a0c
6 changed files with 885 additions and 858 deletions
+8
View File
@@ -7,6 +7,8 @@ export const PORT = 3000;
// Password policy
export const MIN_PASSWORD_LENGTH = 8;
/** bcrypt cost factor. 10 is current Sencho default; roughly ~75ms/hash on modern hardware. */
export const BCRYPT_SALT_ROUNDS = 10;
// Labels
export const VALID_LABEL_COLORS = ['teal', 'blue', 'purple', 'rose', 'amber', 'green', 'orange', 'pink', 'cyan', 'slate'] as const;
@@ -20,6 +22,12 @@ export const MFA_PENDING_COOKIE_NAME = 'sencho_mfa_pending';
export const MFA_PENDING_SCOPE = 'mfa_pending';
export const MFA_PENDING_TTL_MS = 5 * 60 * 1000; // 5 minutes to complete the challenge
// MFA replay-prevention: recently-used codes are blacklisted for
// MFA_REPLAY_TTL_MS to block replay within a single 30-second TOTP window
// (plus drift tolerance). A periodic purge keeps the table bounded.
export const MFA_REPLAY_TTL_MS = 120 * 1000;
export const MFA_REPLAY_PURGE_INTERVAL_MS = 60 * 1000;
// Hot-path cache TTLs.
// Short TTLs collapse concurrent polling pressure across browser tabs and
// overlapping service samplers without introducing noticeable UI staleness.