3.8 KiB
3.8 KiB
plan, phase, status, wave, completed, key-files
| plan | phase | status | wave | completed | key-files | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 07.2-03 | 07.2-security-jti-claim-redis-access-token-revocation-inserted | complete | 2 | 2026-06-06 |
|
Plan 07.2-03 Summary — Wave 2: user_nbf Write Sites
What Was Built
Wired the user_nbf Redis writes into all four security-event handlers that trigger
access-token revocation, completing the full Phase 7.2 revocation pipeline.
Task 1 — backend/api/auth.py:
- Added
import timeat module level (afterimport hashlib) - Added
await request.app.state.redis.set(f"user_nbf:{current_user.id}", int(time.time()), ex=settings.access_token_expire_minutes * 60)immediately beforeawait session.commit()in:change_passwordhandler (usesrequest.app.state.redisdirectly)enable_totphandler (usesredis_clientalready in scope)disable_totphandler (usesrequest.app.state.redisdirectly)
- Promoted all 3 Wave 0 xfail stubs in
test_auth_api.pyto PASSED assertions
Task 2 — backend/api/admin.py:
- Added
import timeat module level (afterimport uuid) - Added
from config import settingsat module level (was previously only locally imported) - Added
await request.app.state.redis.set(f"user_nbf:{user.id}", int(time.time()), ex=settings.access_token_expire_minutes * 60)inside theif not body.is_active:block in the deactivation handler, afterawait revoke_all_refresh_tokens(session, user.id)— does NOT execute on activation path - Promoted both Wave 0 admin xfail stubs in
test_admin_api.pyto PASSED assertions
Task 3 — Full regression run:
- Full backend suite: 387 passed, 1 failed (pre-existing
test_extract_docx—ModuleNotFoundError: No module named 'docx', unrelated to Phase 7.2), 7 xfailed (pre-existing from prior phases), 6 skipped
Patterns Established
- user_nbf write pattern:
await redis.set(f"user_nbf:{user.id}", int(time.time()), ex=settings.access_token_expire_minutes * 60)beforesession.commit() - Conditional deactivation-only write: write is strictly inside
if not body.is_active:— reactivation does NOT write user_nbf (invariant verified bytest_activate_user_does_not_write_user_nbf) - TTL derived from settings:
settings.access_token_expire_minutes * 60— stays synchronized if TTL changes in config, not hardcoded 900
Patterns to Avoid
- Do NOT write
user_nbfon activation/reactivation (is_active=True) — would force legitimate users to re-login immediately - Do NOT hardcode TTL as 900 — use
settings.access_token_expire_minutes * 60
What This Enables
Full Phase 7.2 revocation pipeline is now complete:
- Every access token contains a
jtiUUID claim (Plan 02) get_current_userreadsuser_nbf:{user_id}from Redis and rejects tokens withiat < nbf(Plan 02)- All four security events write
user_nbf:{user_id}to Redis: change_password, enable_totp, disable_totp (this plan), and admin deactivation (this plan)
Any token issued before a security event is blocked on the next request — the 15-minute access-token window is closed.
Self-Check: PASSED
- All 9 VALIDATION.md behaviors: PASSED
grep -c "user_nbf" backend/api/auth.py→ 3 (change_password, enable_totp, disable_totp)grep -c "user_nbf" backend/api/admin.py→ 1 (deactivation only)grep -c "user_nbf" backend/deps/auth.py→ 5 (reader: multiple log/comparison lines)except HTTPException: raiseat line 81 precedesexcept Exceptionat line 83 indeps/auth.py— Pitfall 1 guard confirmedgrep -E "^import time" backend/api/auth.py backend/api/admin.py→ 2 lines- Zero new test failures introduced by Phase 7.2 (pre-existing
test_extract_docxfailure is a missingpython-docxmodule, unrelated)