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
+16
View File
@@ -198,3 +198,19 @@ export function issueMfaPendingCookie(
export function clearMfaPendingCookie(res: Response, req: Request): void {
res.clearCookie(MFA_PENDING_COOKIE_NAME, getCookieOptions(req));
}
/**
* Re-issue the session cookie after bumping `token_version`. Routes that
* call `bumpTokenVersion` (password change, MFA enrol, MFA disable) use this
* so the caller stays signed in after their previous cookie is invalidated.
* No-ops silently when the JWT secret is missing or the user has been
* deleted mid-request.
*/
export function reissueSessionAfterTokenBump(req: Request, res: Response, userId: number): void {
const db = DatabaseService.getInstance();
const refreshed = db.getUserById(userId);
const settings = db.getGlobalSettings();
if (refreshed && settings.auth_jwt_secret) {
issueSessionCookie(res, req, refreshed, settings.auth_jwt_secret);
}
}