auth: security audit fixes, rate limits on every route
ci / check (push) Successful in 1m3s
ci / images (push) Successful in 19s
ci / check (pull_request) Successful in 1m5s
ci / images (pull_request) Successful in 5s

Two separate reviews of the SSO branch and all 207 API routes (spec 12 §8.6). Every route was
guarded as meant. Fixed, each with a test:
- login: a directory 503 counts as a failed sign-in; a locked directory account is a refused
  password, not "unavailable"
- open redirect after OpenID sign-in (a tab in next)
- directory email matching only for non-admins without their own password; others connect on
  My settings (POST /api/me/identities/ldap); only admins disconnect a directory account
- OpenID session check reads the refreshed ID token's claims too
- connect is POST /api/auth/oidc/connect (CSRF header)
- stored bind password, client secret and SMTP password tied to their server
- 409 role_managed when a provider sets the role; empty Contrack PATCH is a 400
- @fastify/rate-limit on every route (RATE_LIMIT_PER_MINUTE), 10/min on Test buttons; a
  password asked again shares the login limiter
Live tests pass against lldap 0.6.3, Samba 4 AD and Keycloak 26.8.1.
This commit is contained in:
iamdoubz committed 2026-10-26 09:46:23 -05:00
1 parent 19610777a4
commit 97e67e347d
39 files changed
+1059 -186

No files matched your search

+6 -1
View File
@@ -33,7 +33,12 @@
- **CSRF:** every non-GET `/api/*` request with cookie auth must carry the header
`X-Requested-With: selfphoneng`. The web client always sends it. Browsers can't send
it cross-site without CORS, and CORS is off.
- Login rate limit: 5 failures per IP per 15 min and 10 per email per hour → HTTP 429.
- Login rate limit: 5 failures per IP per 15 min and 10 per email per hour → HTTP 429. A signed-in
person's password asked again (change password, two-step on/off, recovery codes) counts the same.
- Every route: `@fastify/rate-limit`, `RATE_LIMIT_PER_MINUTE` (default 600) per client IP → 429
`rate_limited`. Test buttons, diagnostics and the source download: 10 a minute (`SLOW` in
`auth/guards.ts`). `/prov` (its own limits, 07 §2) and the caller-name route (Asterisk on this
machine) are outside it (`NO_LIMIT`).
Write `login_failed` to the audit log with the IP.
- If `must_change_password`, every route except `/api/auth/*` returns 403 `{code:'password_change_required'}`.
- Single sign-on (spec 12, as built in S1): the body is `{login, password, totp?}` (`login`: email, or a