Refactor JWT verification and implement session versioning#647
Conversation
- Added session_version to User model to track session state changes. - Updated JWT creation and verification to include session_version claims. - Implemented session invalidation on password reset and user deactivation. - Enhanced JWT verification to check session_version against the current user session. - Updated SSO token generation to include session_version. - Modified API key model to include a deterministic fingerprint for cache invalidation. - Improved tests to cover new session_version logic and cache interactions.
…n logic - Renamed `verify_jwt_token` to `verify_jwt_claims` for clarity in its purpose. - Updated all references to the renamed function across the codebase, including middleware, routers, and tests. - Enhanced `_invalidate_auth_cache` method in the User model to ensure both cache invalidation attempts succeed before considering the operation successful, with added logging for failures. - Added regression tests for the `_invalidate_auth_cache` method to cover various scenarios, including success, transient failures, and critical failure logging. - Updated comments and documentation to reflect changes in the cache invalidation strategy and JWT verification process.
|
@claude please review this PR |
|
Claude finished @jfrench9's task in 4m 24s —— View job PR Review: Refactor JWT verification and implement session versioningTasks
Overall this is a well-structured security refactor with a clear design rationale. The session-versioning approach is sound, the backward-compatibility story is good, and the PR found and fixed at least one pre-existing bug (API key cache invalidation). A few issues worth addressing before merge, plus several smaller notes below. Significant Find: Pre-existing API Key Cache Invalidation Bug — Fixed ✅In Medium Issues1.
# robosystems/middleware/auth/jwt.py
"session_version": _get_user_session_version(user_id, session=session) or 0,vs. every caller having: create_jwt_token(str(user.id), device_fingerprint, session=session)
# user.session_version is right thereThe cleaner approach — one that also eliminates any sync-vs-async session ambiguity — is to accept def create_jwt_token(
user_id: str,
device_fingerprint: dict[str, Any] | None = None,
session_version: int = 0,
) -> str:Callers already have the value: 2. Dead code: The old per-token 3.
Low / Nit4. PR description overstates the breaking change
This is not accurate. 5. The function is documented as returning "cached JWT user data" but actually returns 6. Stale In # cache.py ~line 858
if cached_version != int(session_version):
logger.debug(...)
return None # ← no deleteUnder normal operation 7. Redundant # dependencies.py:358
if user and bool(user.is_active):
8. Session version bump on
Positive Security Notes
Test CoverageThe test restructuring is clean — separating One gap: there's no test for the case where |
|
@claude have we resolved all issues |
|
Claude finished @jfrench9's task in 3m 23s —— View job Tasks
Issue Status ReviewNot all issues from the previous review have been resolved. Here's the current status: ✅ Resolved
❌ Still OpenMedium #1 —
# jwt.py:252 — still queries DB:
"session_version": _get_user_session_version(user_id, session=session) or 0,Medium #3 — The blocking sleep is still there on the retry path. Not critical, but a 50ms thread block on every Redis blip that hits a password reset or deactivation. CodeQL false positive — SHA-256 flagged as "weak password hashing" ( The CodeQL alert is a false positive: Code quality — Silent empty except Exception:
pass # ← no log, no explanationThe Low #5 — The function returns the full auth envelope ( Low #6 — Stale cache entries not evicted on session_version mismatch ( When if cached_version != int(session_version):
logger.debug(...)
return None # ← should also delete cache_key, signature_key hereLow #7 — Redundant if user and bool(user.is_active): # user.is_active is always True here
Low #8 —
Summary4 of the 12 flagged issues are resolved. The 8 remaining are all low-to-medium severity with the most impactful being the redundant DB query in |
Summary
Refactors the JWT verification pipeline and auth cache invalidation logic, introducing session versioning to enable reliable, granular token revocation without relying solely on token blocklists.
Key Changes
Session Versioning
session_versioncolumn to both users and API keys tables via a new Alembic migrationJWT Verification Refactor
Auth Cache Invalidation Improvements
Auth Router Updates
Model Enhancements
UserandUserApiKeymodels with session version fields and methods for version managementBreaking Changes
session_versionclaim. Existing tokens without this claim will fail verification and require re-authentication.session_versioncolumns to the users and API keys tables.verifyfunction return value has changed, which affects any code consuming its output directly (all internal call sites and tests have been updated).Testing
Infrastructure Considerations
session_versioncolumnssession_versionclaim, so a coordinated deployment is recommended🤖 Generated with Claude Code
Branch Info:
refactor/jwt-cache-invalidation-fixmainCo-Authored-By: Claude noreply@anthropic.com