-
-
Notifications
You must be signed in to change notification settings - Fork 116
Investigate and fix issue 744 backend behavior #746
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The Auth0RemoteUserJSONWebTokenBackend.authenticate() method was catching all exceptions including JSONWebTokenExpired and returning None. This caused the GraphQL layer to return a generic "User is not authenticated" error instead of "Signature has expired", preventing the frontend from detecting token expiration and triggering automatic token refresh. Changes: - Re-raise JSONWebTokenExpired in Auth0RemoteUserJSONWebTokenBackend so frontend receives "Signature has expired" message - Add auth_error dict to WebSocket middleware scope with code and message - Add WS_CLOSE_TOKEN_EXPIRED (4001) and WS_CLOSE_TOKEN_INVALID (4002) close codes for WebSocket connections - Add comprehensive tests for token expiration handling Frontend's errorLink.ts already handles "Signature has expired" by clearing auth state and triggering page reload for silent Auth0 token refresh.
PR Review: Token Expiration HandlingThank you for this well-crafted fix for issue #744. This PR properly addresses the token expiration signaling issue between backend and frontend. Here's my detailed review: ✅ Strengths
|
- Updated all WebSocket consumers to check scope['auth_error'] and use the specific close codes (4001 for expired, 4002 for invalid) instead of generic 4000 - Fixed consumers: document_conversation, corpus_conversation, standalone_document_conversation, unified_agent_conversation, thread_updates - Removed unused Union import from middleware.py - Fixed lazy import issue in utils.py for sync_remote_user - Added Auth0 test settings defaults to enable test imports - Cleaned up unused imports in test_token_expiration.py This ensures the frontend can distinguish between expired tokens (requiring refresh) and invalid tokens (requiring re-authentication). Closes #744
Code Review - PR #746: Token Expiration HandlingSummaryThis PR addresses issue #744 by fixing token expiration signaling from backend to frontend. The changes enable proper distinction between expired and invalid tokens, allowing the frontend to trigger appropriate refresh/re-authentication flows. ✅ Strengths1. Excellent Problem Analysis & Solution Design
2. Comprehensive Test Coverage (
|
Changes: - Update tests to expect 4002 (WS_CLOSE_TOKEN_INVALID) for invalid tokens instead of 4000, reflecting the new specific error code behavior - Consolidate duplicate auth_error handling across 5 consumers into shared utility function `check_auth_and_close_if_failed()` in auth_helpers.py - Add `WS_CLOSE_UNAUTHENTICATED` constant (4000) and replace all magic numbers in consumers with the defined constant - Fix inconsistent logging levels: use debug for normal flow, warning only for actual problems (expired/invalid tokens) - Reduce token logging verbosity from 20 to 10 characters - Add clarifying comment about scope lifecycle to address race condition concern (each WebSocket connection gets fresh scope) - Add docstring to Auth0RemoteUserJSONWebTokenBackend explaining why it re-raises JSONWebTokenExpired (designed for graphql_jwt, not Django auth) - Add tests for WS_CLOSE_UNAUTHENTICATED constant consistency - Add new test for no-token public document access (test renamed from test_invalid_token_public_document_connects_as_anonymous) Test files updated: - test_standalone_document_consumer.py: Update expected close codes - test_websocket_auth.py: Import and use WS_CLOSE_TOKEN_INVALID constant - test_token_expiration.py: Add tests for unauthenticated close code
Pull Request Review - Issue #744: Token Expiration SignalingSummaryThis PR effectively addresses issue #744 by ensuring that expired JWT tokens are properly signaled to the frontend via both GraphQL and WebSocket connections. The implementation is well-thought-out and follows good architectural patterns. ✅ Strengths1. Excellent Architecture & Design
2. Comprehensive Testing
3. Detailed Documentation
4. Error Signaling DesignThe scope['auth_error'] pattern is clean: scope['auth_error'] = {
'code': WS_CLOSE_TOKEN_EXPIRED, # 4001
'message': 'Token has expired. Please refresh your session.'
}This allows consumers to distinguish between:
🔍 Code Quality ObservationsMinor Issues
🔒 Security Considerations✅ Good Security Practices
|
…odes - Fix duplicate username 'admin' conflicts in parallel test runs by using unique usernames (agent_result_superuser, corpus_exec_superuser, badge_idor_superuser) - Update WebSocket tests to expect close code 4002 for invalid tokens (aligned with recent auth_error changes) - Convert PydanticAI tests from run_sync() to async/await to avoid "event loop is closed" errors - Fix extract task callback test to patch function directly instead of .si method (works correctly in eager mode)
Code Review: Token Expiration Signaling Fix (Issue #744)SummaryThis PR successfully addresses issue #744 by ensuring that JWT token expiration is properly signaled to the frontend, enabling automatic token refresh. The implementation is well-architected with comprehensive test coverage and excellent documentation. ✅ Strengths1. Excellent Architecture & DRY Principles
2. Comprehensive Test CoverageThe new
3. Outstanding Documentation
4. Proper Error Handling
5. Security Best Practices
🔍 Issues & RecommendationsCritical: Import Error RiskLocation: Issue: While the lazy import fix is good, there's still a potential issue: from opencontractserver.users.tasks import sync_remote_userIf Recommendation: try:
from opencontractserver.users.tasks import sync_remote_user
sync_remote_user.delay(user.username)
except ImportError:
logger.warning("Auth0 sync task unavailable (USE_AUTH0=False?)")Minor: Log Level InconsistencyLocation: Issue: Changed from Recommendation: Consider using Minor: Unused Import CleanupLocation: Issue: The CHANGELOG mentions removing Recommendation: Run Documentation: Missing Frontend Integration GuideIssue: While the backend changes are well-documented, there's no mention of how the frontend's Recommendation: Add a section to the CHANGELOG or a comment in the middleware explaining:
Testing: WebSocket Consumer TestsLocation: Issue: The WebSocket tests verify connection rejection but don't assert the exact close code received: self.assertFalse(connected) # Should also check close_code == WS_CLOSE_TOKEN_EXPIREDRecommendation: Enhance assertions: connected, close_code = await communicator.connect()
self.assertFalse(connected)
self.assertEqual(close_code, WS_CLOSE_TOKEN_EXPIRED)🛡️ Security Assessment✅ No security concerns identified:
⚡ Performance Considerations✅ Minimal performance impact:
📋 Pre-Merge ChecklistBefore merging, verify:
🎯 Overall AssessmentLGTM with minor recommendations. This is high-quality work that follows the project's conventions and solves the problem elegantly. Score: 9/10
Recommended Action: Approve with optional follow-up for the testing enhancement and import error handling. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
The Auth0RemoteUserJSONWebTokenBackend.authenticate() method was catching all exceptions including JSONWebTokenExpired and returning None. This caused the GraphQL layer to return a generic "User is not authenticated" error instead of "Signature has expired", preventing the frontend from detecting token expiration and triggering automatic token refresh.
Changes:
Frontend's errorLink.ts already handles "Signature has expired" by clearing auth state and triggering page reload for silent Auth0 token refresh.