64 lines
2.9 KiB
Markdown
64 lines
2.9 KiB
Markdown
# Phase 7.2: Security — JTI Claim + Redis Access-Token Revocation - Discussion Log
|
|
|
|
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
|
|
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
|
|
|
|
**Date:** 2026-06-05
|
|
**Phase:** 07.2-security-jti-claim-redis-access-token-revocation-inserted
|
|
**Areas discussed:** Revocation scope, Redis injection, Redis failure behavior
|
|
|
|
---
|
|
|
|
## Revocation scope
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Current session only (per-JTI) | Revoke only the JTI in the current request. Other sessions' access tokens stay valid up to 15 min — refresh tokens already gone (Phase 7.1), so no extension possible. | |
|
|
| All sessions (user-level NBF key) | Set `user_nbf:{user_id}` = now() in Redis. `get_current_user` checks `token.iat < nbf` — all tokens issued before the event are immediately invalid. One extra Redis read per request. | ✓ |
|
|
|
|
**Follow-up — TTL for user_nbf key:**
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| 15 minutes (access token TTL) | Keys auto-expire when the last old token would anyway. Minimal Redis footprint. | ✓ |
|
|
| Indefinitely (no TTL) | Extra safety margin; accumulates keys permanently. | |
|
|
|
|
**Follow-up — calling session's own token is also blocked:**
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Transparent refresh is fine | 401 → refresh (still-live cookie) → new token. Current session recovers silently. Clean uniform implementation. | ✓ |
|
|
| Keep current session alive | Requires JTI whitelist, adds complexity. | |
|
|
|
|
**Notes:** User-level NBF cleanly covers all sessions without per-JTI tracking infrastructure. The 401→refresh recovery is transparent because Phase 7.1 D-02 kept the current refresh token alive.
|
|
|
|
---
|
|
|
|
## Redis injection
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| `request.app.state.redis` (inline) | `get_current_user` already has `request: Request`. Consistent with auth.py handler pattern (lines 197, 566). Zero new infrastructure. | ✓ |
|
|
| `get_redis` FastAPI dependency | More testable in isolation but adds a new dependency function and changes `get_current_user` signature. | |
|
|
|
|
**Notes:** Consistency with existing inline pattern was the deciding factor.
|
|
|
|
---
|
|
|
|
## Redis failure behavior
|
|
|
|
| Option | Description | Selected |
|
|
|--------|-------------|----------|
|
|
| Fail-open: allow the request through | Log a warning, skip the check. Same as HIBP pattern. Availability wins. | ✓ |
|
|
| Fail-closed: raise 503 | Security wins; any Redis blip takes down authentication. | |
|
|
|
|
**Notes:** Matches the HIBP fail-open philosophy already established in the codebase.
|
|
|
|
---
|
|
|
|
## Deferred Ideas
|
|
|
|
- Per-JTI Redis tracking (superseded by user-level NBF)
|
|
- Phase 7.3 — ES256 algorithm upgrade
|
|
- Phase 7.4 — Token fingerprinting / token binding
|