Commit Graph
432 Commits
Author SHA1 Message Date
curo1305 25c9142fe0 feat(07.4-02): add _compute_fgp helper + extend create_access_token with fgp claim
- Add _compute_fgp(user_agent, accept_lang) helper returning 16-char hex
  HMAC-SHA256 fingerprint (D-04); uses existing hmac + hashlib imports
- Extend create_access_token signature with user_agent/accept_lang params
  (empty-string defaults for backward compatibility, D-05)
- Embed fgp claim in every issued access token payload
2026-06-06 21:54:47 +02:00
curo1305 8c817705b7 docs(phase-07.4): update tracking after wave 0 — plan 07.4-01 complete 2026-06-06 21:52:35 +02:00
curo1305 1adddf8cf4 chore: merge executor worktree (worktree-agent-a37beb50341e88a90) 2026-06-06 21:51:52 +02:00
curo1305 bbd7a11935 docs(07.4-01): complete Wave 0 xfail stub plan — 4 FGP stubs, SUMMARY.md
- SUMMARY.md: plan metadata, task commit hash, self-check passed
- backend/tests/test_auth_fgp.py committed in 7833edb
2026-06-06 21:50:27 +02:00
curo1305 7833edbf3b test(07.4-01): add Wave 0 xfail stubs for FGP-01..FGP-04
- Create backend/tests/test_auth_fgp.py with 4 xfail(strict=False) stubs
- FGP-01: test_fgp_match_returns_200 (matching headers → 200)
- FGP-02: test_fgp_mismatch_returns_401 (wrong UA → 401)
- FGP-03: test_no_fgp_claim_allowed (no fgp claim → 200 migration grace)
- FGP-04: test_missing_headers_empty_string_binding (empty-string binding → 200)
- All 4 tests report XFAIL; full suite remains at 1 pre-existing failure only
2026-06-06 21:49:46 +02:00
curo1305andClaude Sonnet 4.6 22fcb53d9d docs(07.4): create phase plan — token fingerprinting / token binding
2 plans (2 waves): Wave 0 xfail stubs for test_auth_fgp.py (4 FGP tests),
Wave 1 production implementation (_compute_fgp helper, create_access_token
fgp claim, get_current_user fgp validation, login/refresh call-site updates,
test promotion) + version bump to 0.1.3.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 21:44:21 +02:00
curo1305andClaude Sonnet 4.6 de0f3fb7c3 docs(07.4): create phase plan — 2 plans, Wave 0 xfail stubs + Wave 1 fgp implementation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 20:22:29 +02:00
curo1305 50668cc916 docs(state): record phase 7.4 context session 2026-06-06 19:42:13 +02:00
curo1305 78316b6ed7 docs(07.4): capture phase context 2026-06-06 19:42:08 +02:00
curo1305andClaude Sonnet 4.6 169c2e7e29 docs(phase-07.3): mark complete — 3/3 plans, 9/9 tests, remember_me shipped
Plan 03 human checkpoint passed. 07.3-03-SUMMARY.md written.
ROADMAP: 07.3-03 checked, progress table updated to 3/3 Complete 2026-06-06.
STATE: current_phase → 07.4, completed_phases → 12, completed_plans → 63.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 19:19:17 +02:00
curo1305 739d5b3d9a chore: merge executor worktree (worktree-agent-a9c4907ff53a1a111)
# Conflicts:
#	.env.example
#	README.md
#	backend/config.py
#	backend/main.py
#	backend/tests/test_auth_es256.py
2026-06-06 18:07:31 +02:00
curo1305andClaude Sonnet 4.6 e0f2d6c71f docs(readme): add JWT_PRIVATE_KEY + JWT_PUBLIC_KEY to Quick Start step 3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 18:00:15 +02:00
curo1305 21e5d27c90 feat(07.3-03): frontend remember_me — checkbox + ref threading + store pass-through
- LoginView.vue: add rememberMe ref(false) + "Stay signed in for 30 days" checkbox in password step; thread rememberMe.value through all 3 submit handlers (submitPassword, submitTotp, submitBackupCode) (D-12)
- stores/auth.js: login() forwards options.rememberMe as remember_me in api.login body (D-12)
- api/client.js: no change needed — already forwards full body verbatim
- Frontend build passes (vite build exits 0)
2026-06-06 17:23:02 +02:00
curo1305 9cc11b5446 feat(07.3-03): backend remember_me — TTL split + cookie Max-Age + 3 promoted tests
- services/auth.py: create_refresh_token gains remember_me=False param; selects 16h or 30d TTL (D-09, D-10, D-11)
- api/auth.py: LoginRequest.remember_me bool field; _set_refresh_cookie remember_me param for conditional max_age; login handler threads remember_me through both calls (D-11, RM-03)
- test_auth_es256.py: promote RM-01, RM-02, RM-03 stubs — all 9 phase tests now PASSED
2026-06-06 17:21:14 +02:00
curo1305 8be792ab4c feat(07.3-02): ES256 JWT algorithm upgrade + startup rotation hook
- config.py: add refresh_token_expire_hours=16, jwt_private_key, jwt_public_key fields (D-01, D-09)
- services/auth.py: swap all 4 JWT sites to ES256 via base64-decoded PEM keys; remove HS256 (D-02, D-03)
- main.py: add _rotate_tokens_on_algorithm_change lifespan hook — bulk-revokes refresh tokens on algorithm change; idempotent on repeat boots (D-04, D-05)
- test_auth_es256.py: promote ES256-01..05 + CFG-01 stubs to 6 passing tests; RM-01..03 remain xfail
- docker-compose.yml: inject JWT_PRIVATE_KEY + JWT_PUBLIC_KEY into backend + celery-worker (D-07)
- README.md: add JWT key env vars + key generation Python one-liner snippet
- .env.example: add JWT_PRIVATE_KEY= and JWT_PUBLIC_KEY= lines
- Version bump to 0.1.2
2026-06-06 17:19:50 +02:00
curo1305 99f55825aa test(07.3-01): Wave 0 Nyquist scaffold — 9 xfail stubs for ES256 + remember_me
- Create test_auth_es256.py with 9 xfail(strict=True) stubs: ES256-01..05, RM-01..03, CFG-01
- Add es256_keys(autouse) fixture using cryptography P-256 keygen + monkeypatch with raising=False
- Extend test_settings_has_jwt_config to assert refresh_token_expire_hours==16 + jwt key fields
2026-06-06 17:13:50 +02:00
curo1305andClaude Sonnet 4.6 cc959171ba docs(phase-07.3): update tracking after wave 1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 17:10:37 +02:00
curo1305andClaude Sonnet 4.6 c606191f17 fix(07.3): add global ES256 test key fixture to conftest.py
After the ES256 upgrade, create_access_token requires non-empty
jwt_private_key / jwt_public_key. The auth_user, second_auth_user,
and admin_user fixtures in conftest.py call create_access_token
without keys being set, breaking all tests that exercise the auth
system. Add a function-scoped autouse fixture that provisions a
generated P-256 keypair for every test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 17:10:28 +02:00
curo1305 0f01a7aa82 chore: merge executor worktree (worktree-agent-ad0ae14e8e88d89cf) 2026-06-06 17:05:56 +02:00
curo1305 e7e3f527a5 docs(07.3-02): complete plan 02 SUMMARY — ES256 core upgrade + startup rotation
6 tests promoted from xfail to passing (ES256-01..05 + CFG-01); 3 remain xfail for Plan 03
2026-06-06 17:05:31 +02:00
curo1305 0d1ab05e45 chore(07.3-02): wire JWT env vars in docker-compose, README key-gen, .env.example, bump to 0.1.2
- docker-compose.yml: add JWT_PRIVATE_KEY + JWT_PUBLIC_KEY to backend and celery-worker service env blocks
- README.md: add JWT_PRIVATE_KEY/JWT_PUBLIC_KEY to required env vars table; add JWT Key Generation section with Python one-liner and warning about session revocation
- .env.example: add JWT_PRIVATE_KEY= and JWT_PUBLIC_KEY= adjacent to SECRET_KEY
- backend/main.py: version bump 0.1.1 -> 0.1.2
- frontend/package.json: version bump 0.1.1 -> 0.1.2
2026-06-06 17:03:59 +02:00
curo1305 8d261b0509 security(07.3-02): add _rotate_tokens_on_algorithm_change lifespan hook + promote tests
- backend/main.py: add import logging, select; add _rotate_tokens_on_algorithm_change helper above lifespan; call from lifespan with try/except wrap
- backend/tests/test_auth_es256.py: promote ES256-04 and ES256-05 startup rotation tests from xfail to passing; suite reports 6 PASSED + 3 XFAILED
2026-06-06 17:02:45 +02:00
curo1305 fd3f611546 security(07.3-02): ES256 signing — swap 4 JWT sites + config keys + promote tests
- backend/config.py: add refresh_token_expire_hours=16, jwt_private_key, jwt_public_key
- backend/services/auth.py: add import base64; all 4 JWT sites use ES256 with inline PEM decode; zero HS256/secret_key references
- backend/tests/test_auth_es256.py: promote ES256-01..03 + CFG-01 tests from xfail to passing
2026-06-06 16:59:38 +02:00
curo1305andClaude Sonnet 4.6 1eb1d59c8d docs(phase-07.3): update tracking after wave 0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 16:54:48 +02:00
curo1305 b9e1f2a464 chore: merge executor worktree (worktree-agent-a6bcbfd85b592800f) 2026-06-06 16:53:02 +02:00
curo1305 b1e3d0bc73 docs(07.3-01): add plan execution SUMMARY.md
- Documents 9 xfail stubs created + test_settings_has_jwt_config extension
- Records test counts: 372 passed / 1 failed / 9 xfailed
- Includes promotion plan mapping each stub to its Plan 02/03 task
- Confirms no production code was modified in this Wave 0 plan
2026-06-06 16:51:40 +02:00
curo1305 fac0e781f9 test(07.3-01): extend test_settings_has_jwt_config — red until Plan 02
- Assert settings.refresh_token_expire_hours == 16 (CFG-01 / D-09)
- Assert hasattr(settings, "jwt_private_key") (CFG-01 / D-01)
- Assert hasattr(settings, "jwt_public_key") (CFG-01 / D-01)
- Existing refresh_token_expire_days == 30 assertion preserved (Pitfall 5)
- Test intentionally fails until Plan 02 adds the three new config fields
2026-06-06 16:50:32 +02:00
curo1305 5c1a1f9504 test(07.3-01): add Wave 0 xfail scaffold — 9 ES256/RM/CFG stubs
- Create backend/tests/test_auth_es256.py with 9 xfail(strict=True) stubs
- Cover ES256-01..05 (access token, HS256 rejection, reset token, startup rotation)
- Cover RM-01..03 (16h default TTL, 30d remember_me TTL, cookie max_age)
- Cover CFG-01 satellite (jwt_private_key/jwt_public_key presence)
- Add es256_keys autouse fixture generating fresh P-256 keypair per test
- All 9 stubs collect and run as XFAIL; zero XPASS, zero ERROR
2026-06-06 16:49:58 +02:00
curo1305andClaude Sonnet 4.6 4d0ada6d06 docs(phase-07.2): mark VERIFICATION.md passed — CR-02 gap resolved
Gap (password_reset_confirm missing user_nbf write) was fixed during UAT
phase: request: Request param added + Redis write at auth.py:744-750.
VALIDATION.md: 84/84 tests, nyquist_compliant=true. VERIFICATION.md
updated from gaps_found → passed (9/9). STATE.md advanced to phase 07.3.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 12:37:59 +02:00
curo1305andClaude Sonnet 4.6 2265aaaba4 docs(phase-07.2): mark VALIDATION.md complete — nyquist_compliant: true
Audited post-execution state: all 11 tasks (9 original + CR-02 gap fix +
activate anti-pattern guard) have green automated tests. 84/84 passing.
Zero xfail stubs remaining. Sign-off approved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 11:54:57 +02:00
curo1305andClaude Sonnet 4.6 b8e0e3adec test(07.2): complete automated UAT — 7/7 pass
All Phase 7.2 behaviors verified end-to-end against the running app:
- JTI UUID claim present in every issued access token
- Token rejected (Session invalidated) after password change
- Refresh cookie survives password change (skip_token_hash)
- Token rejected after enable-TOTP and disable-TOTP
- Token rejected after admin deactivation
- Reactivation does NOT write user_nbf (no spurious revocation)

Findings recorded in UAT.md:
- Workers must be restarted after Phase 7.2 deploy (--workers 2 does not hot-reload)
- D-02 same-second edge case confirmed benign in real usage
- Deactivation fires is_active check before user_nbf in same-second scenarios (belt-and-suspenders)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 11:47:59 +02:00
curo1305andClaude Sonnet 4.6 95c6db5c42 security(07.2): add SECURITY.md audit — JTI claim + Redis NBF revocation
9/9 threats CLOSED; 11/11 Phase 7.2 tests PASSED; bandit 0 HIGH; full
391-test suite green. Documents gap closure for CR-02 (password_reset_confirm
user_nbf write). HS256→ES256 deferred to Phase 7.3.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 00:33:31 +02:00
curo1305 646736d4d3 docs(phase-07.2): complete phase execution 2026-06-06 00:12:09 +02:00
curo1305 8e4e199062 docs(07.2): add phase verification report 2026-06-06 00:11:44 +02:00
curo1305 d3deef4f95 fix(07.2): add user_nbf write to password_reset_confirm — CR-02 gap closure 2026-06-06 00:11:41 +02:00
curo1305 7e23a3ed0e docs(07.2): add code review report 2026-06-06 00:07:51 +02:00
curo1305 a20d27123d docs(07.2): update tracking after Wave 2 — plan 07.2-03 complete 2026-06-06 00:04:58 +02:00
curo1305 d3588ba055 docs(07.2-03): complete Wave 2 plan — user_nbf write sites in all 4 security-event handlers SUMMARY 2026-06-06 00:04:45 +02:00
curo1305 ae11a6e913 security(07.2-03): add import time + user_nbf write to admin deactivation handler; promote stubs to PASSED 2026-06-06 00:02:27 +02:00
curo1305 efeb75b279 feat(07.2-03): add import time + user_nbf writes to api/auth.py (change_password, enable_totp, disable_totp) 2026-06-06 00:01:19 +02:00
curo1305 2665a5085d docs(07.2): update tracking after Wave 1 — plan 07.2-02 complete 2026-06-05 19:21:48 +02:00
curo1305 9f90d46649 chore: merge executor worktree (worktree-agent-a9a1eb8685f2efc98) — Wave 1 plan 07.2-02 2026-06-05 19:21:09 +02:00
curo1305 61199752b5 docs(07.2-02): complete Wave 1 plan — jti claim + user_nbf NBF check SUMMARY
- jti UUID claim in create_access_token (D-01)
- user_nbf Redis NBF check in get_current_user with fail-open + re-raise guard (D-02, D-03, D-04, T-7.2-02)
- 4 Wave 0 xfail stubs promoted to PASS; 29 tests passing in target suites
2026-06-05 19:20:42 +02:00
curo1305 30ad9fd015 security(07.2-02): add user_nbf Redis NBF check to get_current_user
- Added import logging + _logger = logging.getLogger(__name__) to deps/auth.py
- Inserted user_nbf Redis check block after decode_access_token in get_current_user
- Check reads user_nbf:{payload['sub']} from Redis; rejects if payload['iat'] < stored timestamp
- Uses strict < comparison (token issued exactly at event second is allowed — D-02)
- Handles both bytes and str returns from Redis for FakeRedis/aioredis compatibility
- except HTTPException: raise guard precedes broad except Exception (T-7.2-02 Pitfall 1)
- Broad except logs warning and fails open (D-04 — Redis outage must not block requests)
- Promoted all 3 Wave 0 NBF xfail stubs to passing assertions in test_auth_deps.py
2026-06-05 19:19:06 +02:00
curo1305 c00c1fbaa7 feat(07.2-02): add jti UUID claim to create_access_token
- Added "jti": str(uuid.uuid4()) to the access token payload in services/auth.py
- Promoted Wave 0 xfail stub test_create_access_token_includes_jti_claim to PASS
- Added test_create_access_token_jti_is_unique_per_call uniqueness guard
- uuid module was already imported at module top — no new imports needed
2026-06-05 19:15:01 +02:00
curo1305 a80b632ac8 docs(07.2): update tracking after Wave 0 — plan 07.2-01 complete 2026-06-05 19:12:41 +02:00
curo1305 8d44018f40 chore: merge executor worktree (worktree-agent-a2f182fa26b9780c8) — Wave 0 plan 07.2-01 2026-06-05 19:11:20 +02:00
curo1305 f0ba9e6d8e docs(07.2-01): complete Wave 0 test scaffolding plan — SUMMARY
- FakeRedis mounted on test_auth_deps and admin_client fixtures
- 9 xfail(strict=False) stubs covering all Phase 7.2 behaviors
- 73 baseline tests pass, zero regressions
2026-06-05 19:10:03 +02:00
curo1305 eb1647293f test(07.2-01): add FakeRedis to admin_client + NBF-write xfail stubs for deactivation
- Import FakeRedis from tests.test_auth_api (no redefinition)
- Mount app.state.redis = FakeRedis() in admin_client fixture before yield
- Reset app.state.redis = None in teardown (mirrors authed_client convention)
- Add test_deactivate_user_writes_user_nbf_to_redis (xfail strict=False)
- Add test_activate_user_does_not_write_user_nbf (negative-guard, xfail strict=False)
- All 22 existing admin tests unaffected (zero regressions)
2026-06-05 19:08:27 +02:00
curo1305 097cdcadf8 test(07.2-01): add NBF-write xfail stubs for change_password/enable_totp/disable_totp
- Add test_change_password_writes_user_nbf_to_redis (xfail strict=False)
- Add test_enable_totp_writes_user_nbf_to_redis (xfail strict=False)
- Add test_disable_totp_writes_user_nbf_to_redis (xfail strict=False)
- Each test exercises the full auth flow then asserts user_nbf Redis key
- Wave 2 (Plan 03) promotes stubs by replacing pytest.xfail() with real assertion
- All 27 existing auth API tests unaffected
2026-06-05 19:07:30 +02:00