mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
fix: address the release-prep review of #684
Re-read the per-IP failure count after the scrypt await, so concurrent failures from one IP are all counted (regression test added). Reword the (username, IP) bucket comment: it grows with failed verifies per pair, not no faster than userFailures. Reword the verify-first test comment: only the per-username bucket never denies a correct password. Correct docs/multi-user-plan.md: the per-username bucket does not stop cross-IP brute force; the per-(username, IP) bucket caps verified guesses. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
b7897b6d0e
commit
9dec12b6e7
3 files changed
+29
-8
No files matched your search
@@ -105,7 +105,7 @@ Keep the existing single-user branch untouched. Add a parallel multi-user branch
|
||||
1. **Credential check**: Basic header parsed into `username:password`, verified against the user store (scrypt + `timingSafeEqual`). Disabled users fail closed.
|
||||
2. **Cookie sessions**: same `codeman_session` cookie and `StaleExpirationMap`, but `AuthSessionRecord` gains `username` and `role`. All existing TTL/sliding/eviction logic reused. Eviction cap becomes per-user aware (evict oldest _of that user_ first) so one user cannot flush everyone's sessions by logging in 100 times.
|
||||
3. **Request identity**: decorate `req.authUser = { username, role }` (Fastify decorateRequest). In single-user mode `req.authUser` is `{ username: 'admin', role: 'admin' }` when auth is on, and a synthetic admin when auth is off, so downstream code has ONE code path.
|
||||
4. **Rate limiting**: keep the per-IP bucket; add a per-username failure bucket (same `StaleExpirationMap` pattern) so a botnet cannot brute-force one account across IPs, and one flaky user behind a NAT cannot lock out the rest.
|
||||
4. **Rate limiting**: keep the per-IP bucket; add a per-username failure bucket (same `StaleExpirationMap` pattern) so one flaky user behind a NAT cannot lock out the rest. As built, the per-username bucket is checked after the verify: it answers 429 to failed guesses once full and never denies a correct password, so it does not cap verified guesses. What caps them is a per-(username, IP) bucket checked before the verify (10 per pair), beside a per-IP pre-gate of 50 (#684).
|
||||
5. **`mustChangePassword` gate**: when set, every API request except `GET /api/me`, `POST /api/me/password`, and static assets returns 403 with `errorCode: 'PASSWORD_CHANGE_REQUIRED'`; the frontend intercepts that code and shows the change-password modal.
|
||||
6. **Password change vs Basic-auth caching**: browsers cache Basic credentials. After a password change we revoke all of that user's cookie sessions; the next request falls to Basic with stale creds, gets 401, and the browser re-prompts. Acceptable for v1; a proper login form is Phase 6 (see 15).
|
||||
7. **Unchanged**: hook-secret loopback bypass (hooks authenticate the _instance_, not a user; the event maps to a session which has an owner), host guard, Origin/CSRF guard, security headers.
|
||||
|
||||
Reference in new issue
Block a user