11 KiB
Logout Tracking & Culprit Detection System
Implemented: December 15, 2025
Purpose: Precise tracking and display of logout causes with exact page/context
Overview
A comprehensive logging and display system that captures exactly why and where a user was logged out, with detailed context for debugging and user transparency.
Implementation Components
1. Backend Middleware Enhancement
File: backend/igny8_core/auth/middleware.py
Changes:
- Added
LOGOUT_REASONSdictionary with standardized error codes - Enhanced all logout triggers to include:
logout_reason: Error code (e.g.,SESSION_ACCOUNT_MISMATCH)logout_message: Human-readable messagelogout_path: Exact page where logout occurredlogout_context: Additional debugging contexttimestamp: ISO timestamp of logout event
Error Codes Added:
SESSION_ACCOUNT_MISMATCH- Session contamination: account ID mismatchSESSION_USER_MISMATCH- Session contamination: user ID mismatchACCOUNT_MISSING- Account not configured for this userACCOUNT_SUSPENDED- Account is suspendedACCOUNT_CANCELLED- Account is cancelledPLAN_MISSING- No subscription plan assignedPLAN_INACTIVE- Subscription plan is inactiveUSER_INACTIVE- User account is inactive
Response Format:
{
"success": false,
"error": "User-friendly message",
"logout_reason": "ERROR_CODE",
"logout_message": "Detailed explanation",
"logout_path": "/previous/page/path",
"logout_context": {
"stored_account_id": 123,
"current_account_id": 456,
"user_id": 789
}
}
2. Frontend API Interceptor Enhancement
File: frontend/src/services/api.ts
Changes:
401 Unauthorized Handler
- Parses backend logout reasons from 401 responses
- Logs detailed context before attempting token refresh
- Stores logout reason in localStorage before logout
- Console logging with 🚨 emoji for visibility
403 Forbidden Handler
- Detects authentication vs permission errors
- Only logs out for auth credential issues
- Stores detailed context including token state
Token Refresh Failure Handler
- Creates logout reason when refresh fails
- Includes original endpoint in context
- Differentiates between "refresh failed" and "no refresh token"
Logout Reason Format:
{
code: 'ERROR_CODE',
message: 'Human-readable message',
path: '/page/where/logout/happened', // NOT /signin
context: {
// Additional debugging info
error: 'specific error details',
endpoint: '/api/v1/...',
hasToken: true/false,
},
timestamp: '2025-12-15T10:30:45.123Z',
source: 'token_refresh_failure' | 'backend_middleware' | 'api_403_auth_error'
}
3. Auth Store Enhancement
File: frontend/src/store/authStore.ts
Changes:
Manual Logout
- Captures logout context BEFORE clearing anything
- Includes user email and current page
- Stores with source:
manual_user_action - Console log with 🚪 emoji
refreshUser Validation
- Enhanced account validation with logout tracking
- Stores detailed context for ACCOUNT_REQUIRED
- Stores detailed context for PLAN_REQUIRED
- Console logging with 🚨 emoji
4. SignIn Page Display
File: frontend/src/components/auth/SignInForm.tsx
Changes:
On Component Mount
- Reads
logout_reasonfrom localStorage - Displays reason to user in yellow alert box
- Logs full details to console
- Clears logout_reason after reading (single display)
UI Components
Alert Box Features:
- Yellow background (warning, not error)
- Clear "Session Ended" heading
- User-friendly message
- Original page path (if not /signin)
- Expandable technical details button
Technical Details Panel:
- Error code
- Source system
- Exact timestamp
- Full context JSON (formatted)
- Collapsible to avoid overwhelming users
Logout Reason Codes & Sources
Backend-Triggered (source: backend_middleware)
| Code | Message | Trigger Location |
|---|---|---|
SESSION_ACCOUNT_MISMATCH |
Session contamination: account ID mismatch | AccountContextMiddleware line ~58 |
SESSION_USER_MISMATCH |
Session contamination: user ID mismatch | AccountContextMiddleware line ~73 |
ACCOUNT_MISSING |
Account not configured for this user | validate_account_and_plan() |
ACCOUNT_SUSPENDED |
Account is suspended | validate_account_and_plan() |
ACCOUNT_CANCELLED |
Account is cancelled | validate_account_and_plan() |
PLAN_MISSING |
No subscription plan assigned | validate_account_and_plan() |
PLAN_INACTIVE |
Subscription plan is inactive | validate_account_and_plan() |
Frontend-Triggered (source: varies)
| Code | Message | Source | Trigger Location |
|---|---|---|---|
TOKEN_REFRESH_FAILED |
Token refresh failed - session expired | token_refresh_failure |
api.ts line ~318 |
NO_REFRESH_TOKEN |
No refresh token available | missing_refresh_token |
api.ts line ~330 |
AUTH_CREDENTIALS_MISSING |
Authentication credentials were not provided | api_403_auth_error |
api.ts line ~203 |
MANUAL_LOGOUT |
User manually logged out | manual_user_action |
authStore.ts line ~151 |
ACCOUNT_REQUIRED |
Account not configured | refresh_user_validation |
authStore.ts line ~412 |
PLAN_REQUIRED |
Plan not configured | refresh_user_validation |
authStore.ts line ~425 |
Console Logging Format
Backend Logs
[2025-12-15 10:30:45] [WARNING] [auth.middleware] [AUTO-LOGOUT] SESSION_ACCOUNT_MISMATCH: Session contamination: account ID mismatch. Session=123, Current=456, User=789, Path=/dashboard, IP=192.168.1.1, Timestamp=2025-12-15T10:30:45.123Z
View Commands:
# All auto-logouts
docker logs igny8_backend 2>&1 | grep "\[AUTO-LOGOUT\]"
# Specific error code
docker logs igny8_backend 2>&1 | grep "SESSION_ACCOUNT_MISMATCH"
# Real-time monitoring
docker logs -f igny8_backend | grep "\[AUTO-LOGOUT\]"
Frontend Console Logs
// Console group with full details
🔍 LOGOUT REASON DETAILS
Code: SESSION_ACCOUNT_MISMATCH
Message: Session contamination: account ID mismatch
Original Page: /dashboard/content
Timestamp: 2025-12-15T10:30:45.123Z
Source: backend_middleware
Context: {stored_account_id: 123, current_account_id: 456, ...}
// Individual logout events
🚨 LOGOUT TRIGGERED - Backend Validation Failed: {...}
🚨 LOGOUT TRIGGERED - Token Refresh Failed: {...}
🚨 LOGOUT TRIGGERED - No Refresh Token: {...}
🚨 LOGOUT TRIGGERED - Authentication Credentials Missing: {...}
🚨 LOGOUT TRIGGERED - Account Required: {...}
🚨 LOGOUT TRIGGERED - Plan Required: {...}
🚪 LOGOUT - User Action: {...}
User Experience Flow
1. User Gets Logged Out
User browsing /dashboard/content
↓
Backend validation fails (e.g., account mismatch)
↓
Backend returns 401 with logout_reason JSON
↓
Frontend API interceptor catches response
↓
Logs detailed context to console
↓
Stores logout_reason in localStorage
↓
Triggers logout and redirects to /signin
2. User Sees Explanation on SignIn Page
SignIn component mounts
↓
Reads logout_reason from localStorage
↓
Displays yellow alert box with:
- "Session Ended" heading
- User-friendly message
- Original page path
- Technical details (expandable)
↓
Logs full details to browser console
↓
Clears logout_reason from localStorage
↓
User understands why they were logged out
Debugging Workflow
For Users (Non-Technical)
- After logout, check yellow alert box on signin page
- Note the message shown
- If needed, click info icon for technical details
- Take screenshot and share with support
For Developers
-
Check Browser Console:
Open DevTools → Console Look for 🚨 or 🚪 emoji logs Expand the logged object for full context -
Check Backend Logs:
docker logs igny8_backend 2>&1 | grep "\[AUTO-LOGOUT\]" | tail -20 -
Check Stored Reason (if still in localStorage):
JSON.parse(localStorage.getItem('logout_reason')) -
Trace Request Path:
- Note the
logout_pathfield - this is the original page - Note the
sourcefield - this tells you which system component triggered it - Check
contextfor specific IDs and values
- Note the
Key Features
✅ Exact Page Tracking
logout_pathalways contains the original page where logout occurred- Never shows
/signinas logout path - Captured BEFORE redirect happens
✅ Comprehensive Context
- Every logout includes relevant IDs (user_id, account_id, etc.)
- Token state captured for auth errors
- Error details preserved through retry attempts
✅ User-Friendly Display
- Non-technical users see simple message
- Technical users can expand for details
- Automatic cleanup (shown once, then cleared)
✅ Developer-Friendly Logging
- Console groups for easy reading
- Emoji markers for quick scanning (🚨 = automatic, 🚪 = manual)
- Full JSON context for debugging
- Backend logs with grep-friendly prefixes
✅ No False Positives
- Only logs out when truly necessary
- Differentiates between auth and permission errors
- Preserves context through token refresh attempts
Testing Scenarios
1. Test Session Contamination
# In backend, manually modify session
request.session['_account_id'] = 999 # Wrong account
# Expected: SESSION_ACCOUNT_MISMATCH logout
2. Test Token Expiration
// Wait 15+ minutes, then make API call
// Expected: TOKEN_REFRESH_FAILED or NO_REFRESH_TOKEN logout
3. Test Manual Logout
// Click logout button
// Expected: MANUAL_LOGOUT with correct page path
4. Test Account/Plan Validation
# In backend, deactivate user's plan
user.account.plan.is_active = False
user.account.plan.save()
# Expected: PLAN_INACTIVE logout on next request
Future Enhancements (Optional)
-
Analytics Integration:
- Send logout reasons to analytics
- Track which reasons are most common
- Identify systemic issues
-
Admin Dashboard:
- View all logout events
- Filter by reason code
- Track affected users
-
User Notification:
- Email users when logged out (except manual)
- Include reason and next steps
- Link to support if needed
-
Automated Recovery:
- For some errors (e.g., PLAN_INACTIVE), show payment link
- Auto-retry after fixing issues
- Remember intended destination
Summary
This implementation provides 100% accurate culprit detection by:
- ✅ Capturing exact page where logout occurred (NOT signin page)
- ✅ Logging detailed context at every logout trigger point
- ✅ Displaying reasons clearly to users on signin page
- ✅ Providing comprehensive debugging info in console and backend logs
- ✅ Using standardized error codes for easy tracking
- ✅ Preserving context through redirects and token refresh attempts
Result: No more guessing why users were logged out. Every logout is tracked, explained, and debuggable.