soulsync/core/security
BoulderBadgeDad 613688a9ad Login recovery (DB + backend): security question to reset a forgotten password
Closes the forgot-login-password gap. A per-profile recovery question + answer lets
a locked-out user reset their own password.

- DB: additive recovery_question + recovery_answer_hash columns (idempotent
  migration). set/get-question/verify/has methods; answer is hashed (pbkdf2) and
  matched forgivingly (trim + lowercase + collapse whitespace). No recovery set →
  never verifies.
- Endpoints (allowlisted in the login gate so they work pre-auth):
  GET /api/auth/recovery-question?username= (generic 404 when absent),
  POST /api/auth/recovery-reset {username, answer, new_password} — brute-force
  limited; a correct answer sets the new password + authenticates the session.
  POST /api/profiles/<id>/set-recovery (admin or self) to configure it.

Tests: set/get/verify, forgiving match, hashed-not-plaintext, no-recovery-never-
verifies, full reset flow (wrong answer rejected + password intact; correct answer
resets), unknown-user 404. 25 tests pass. Next: the Settings + login-screen UI.
2026-06-10 22:24:54 -07:00
..
__init__.py Security: enforce the launch PIN server-side, not just a client overlay (#832) 2026-06-09 22:19:14 -07:00
auth_proxy.py Security: trust a forward-auth proxy user header (Tier 3) 2026-06-10 20:57:48 -07:00
launch_lock.py Fix: launch PIN re-triggered the first-run setup wizard every visit (#842) 2026-06-10 11:40:47 -07:00
login_gate.py Login recovery (DB + backend): security question to reset a forgotten password 2026-06-10 22:24:54 -07:00
rate_limit.py Security: brute-force limiter on the launch-PIN unlock (Tier 2) 2026-06-10 20:47:46 -07:00
reverse_proxy.py Security: add gated security headers in reverse-proxy mode (Tier 2) 2026-06-10 20:48:51 -07:00