Files
igny8/docs/logout-issues/LOGOUT-CAUSES-COMPLETE-REFERENCE.md
IGNY8 VPS (Salman) 69c0fd8b69 reorg
2025-12-17 00:27:53 +00:00

17 KiB

Complete User Logout Causes Reference

Last Updated: December 15, 2025
System: IGNY8 Platform
Purpose: 100% accurate comprehensive reference for all possible logout triggers


Executive Summary

This document catalogs every confirmed cause of user logout in the IGNY8 system, based on analysis of the complete codebase, middleware, authentication flow, and frontend interceptors. Each cause is verified against actual implementation.


All Logout Causes (One Per Line)

Backend-Triggered Automatic Logouts

  1. Session contamination detected - account ID mismatch between session and current user
  2. Session contamination detected - user ID mismatch between session and current user
  3. Account missing or not configured for authenticated user (non-admin, non-developer, non-system users)
  4. Account status is "suspended"
  5. Account status is "cancelled"
  6. No subscription plan assigned to account
  7. Subscription plan is inactive (is_active=False)
  8. JWT access token expired (15 minutes default expiry)
  9. JWT refresh token expired (30 days default expiry)
  10. Invalid JWT token signature
  11. JWT token type mismatch (e.g., refresh token used where access token expected)
  12. User not found in database (user_id from JWT doesn't exist)
  13. Account not found in database (account_id from JWT doesn't exist)
  14. Django session expired (24 hours default - SESSION_COOKIE_AGE)
  15. Session cookie deleted or cleared by browser
  16. Session data corrupted or invalid
  17. User account set to inactive (is_active=False)

Frontend-Triggered Logouts

  1. User clicks logout button (manual logout action)
  2. HTTP 401 Unauthorized response + refresh token missing or invalid
  3. HTTP 401 Unauthorized response + refresh token refresh attempt failed
  4. HTTP 403 Forbidden with "Authentication credentials" error message when user has auth token
  5. HTTP 403 Forbidden with "not authenticated" error message when user has auth token
  6. Token refresh fails with invalid refresh token
  7. Token refresh fails with expired refresh token
  8. Token refresh fails with user not found error
  9. Token refresh fails with network/server error
  10. LocalStorage cleared by user or browser
  11. Browser cache/cookies cleared
  12. Authentication state validation error during app initialization
  13. Account missing from user data during store refresh
  14. Plan missing from account data during store refresh

Security & Policy Triggers

  1. CORS policy violation (cross-origin request blocked)
  2. CSRF token mismatch (for session-based auth on protected endpoints)
  3. Cookie security policy mismatch (SESSION_COOKIE_SECURE vs HTTP/HTTPS)
  4. Cookie SameSite policy enforcement (SESSION_COOKIE_SAMESITE=Strict)
  5. Multiple simultaneous login sessions (if session backend is file-based and user logs in from different device)

Error & Exception Handling

  1. Unhandled authentication exception in middleware
  2. Database connection failure during user/account lookup
  3. Redis session backend failure or unavailable
  4. Authentication backend module import error
  5. JWT library not available (ImportError)
  6. Secret key missing or invalid (JWT_SECRET_KEY / SECRET_KEY)

Rate Limiting & Throttling (Currently Disabled)

  1. Rate limit exceeded (CURRENTLY DISABLED - DebugScopedRateThrottle returns True for all requests)

Note: Throttling is currently disabled in the codebase. The DebugScopedRateThrottle.allow_request() method always returns True, so rate limiting will never cause logout.


Configuration Reference Table

Configuration Value Location User Type Description
SESSION_COOKIE_NAME 'igny8_sessionid' settings.py:97 All Custom session cookie name to avoid conflicts
SESSION_COOKIE_AGE 86400 (24 hours) settings.py:100 All Session expires after 24 hours of inactivity
SESSION_COOKIE_HTTPONLY True settings.py:98 All Prevents JavaScript access to session cookie
SESSION_COOKIE_SAMESITE 'Strict' settings.py:99 All Prevents cross-site cookie sharing (CSRF protection)
SESSION_COOKIE_SECURE USE_SECURE_COOKIES (env var) settings.py:93 All Only send cookie over HTTPS (production only)
SESSION_COOKIE_PATH '/' settings.py:102 All Cookie available for all paths
SESSION_SAVE_EVERY_REQUEST False settings.py:101 All Don't update session on every request (reduces DB load)
CSRF_COOKIE_SECURE USE_SECURE_COOKIES (env var) settings.py:94 All Only send CSRF cookie over HTTPS (production only)

JWT Token Configuration

Configuration Value Location User Type Description
JWT_SECRET_KEY SECRET_KEY (fallback) settings.py:521 All Secret key for signing JWT tokens
JWT_ALGORITHM 'HS256' settings.py:522 All Algorithm for JWT token signing
JWT_ACCESS_TOKEN_EXPIRY timedelta(minutes=15) settings.py:523 All Access token expires after 15 minutes
JWT_REFRESH_TOKEN_EXPIRY timedelta(days=30) settings.py:524 All Refresh token expires after 30 days

Authentication Backend Configuration

Configuration Value Location User Type Description
AUTHENTICATION_BACKENDS ['igny8_core.auth.backends.NoCacheModelBackend'] settings.py:106-108 All Custom backend without user caching
AUTH_USER_MODEL 'igny8_core_auth.User' settings.py:77 All Custom user model with account FK

REST Framework Authentication Order

Order Authentication Class Location User Type Description
1 APIKeyAuthentication settings.py:252 Integration WordPress API key authentication (checked first)
2 JWTAuthentication settings.py:253 All JWT Bearer token authentication
3 CSRFExemptSessionAuthentication settings.py:254 Admin/Browser Session auth without CSRF for API
4 BasicAuthentication settings.py:255 Debug/Test Basic auth as fallback

Permission Classes

Permission Class Location Applies To Bypass For Description
IsAuthenticatedAndActive settings.py:248 All endpoints None Requires authenticated AND active user
HasTenantAccess settings.py:249 All endpoints Superusers, Developers, System Account Requires valid account assignment

Account & Plan Validation Rules

Rule Enforced By Status Codes Bypass For Description
Account must exist AccountContextMiddleware 403 Forbidden Superusers, Developers, System Account User must have account configured
Account status allowed validate_account_and_plan() 403 Forbidden Superusers, Developers, System Account Blocks "suspended" and "cancelled" accounts
Plan must exist validate_account_and_plan() 402 Payment Required Superusers, Developers, System Account Account must have plan assigned
Plan must be active validate_account_and_plan() 402 Payment Required Superusers, Developers, System Account Plan.is_active must be True

Middleware Execution Order

Order Middleware Location Purpose
1 SecurityMiddleware settings.py:111 Django security headers
2 WhiteNoiseMiddleware settings.py:112 Static file serving
3 CorsMiddleware settings.py:113 CORS policy enforcement
4 SessionMiddleware settings.py:114 Session handling
5 CommonMiddleware settings.py:115 Common Django middleware
6 CsrfViewMiddleware settings.py:116 CSRF protection
7 AuthenticationMiddleware settings.py:117 Sets request.user
8 HistoryRequestMiddleware settings.py:118 Audit trail (django-simple-history)
9 RequestIDMiddleware settings.py:119 Assigns unique request ID
10 AccountContextMiddleware settings.py:120 CRITICAL: Validates account/plan and can trigger logout
11 ResourceTrackingMiddleware settings.py:122 Debug resource tracking
12 MessagesMiddleware settings.py:123 Django messages
13 ClickjackingMiddleware settings.py:124 X-Frame-Options header

Note: AccountContextMiddleware is where most automatic logouts occur for account/plan validation failures.

CORS Configuration

Configuration Value Location Description
CSRF_TRUSTED_ORIGINS ['https://api.igny8.com', 'https://app.igny8.com', 'http://localhost:8011', ...] settings.py:79-83 Trusted origins for CSRF validation

Error Response Status Codes That May Trigger Frontend Logout

Status Code Handler Location Frontend Action Description
401 Unauthorized api.ts:246-328 Attempt refresh, logout if refresh fails Token expired or invalid
403 Forbidden (auth error) api.ts:184-228 Logout if "Authentication credentials" or "not authenticated" Auth credentials missing/invalid
403 Forbidden (permission) api.ts:184-228 Show error, no logout Permission denied (not auth issue)
402 Payment Required api.ts:230-244 Show error, no logout Plan/limits issue

Frontend Token Storage

Storage Keys Location Description
localStorage 'auth-storage' authStore.ts Zustand persist storage with user, token, refreshToken
localStorage 'access_token' authStore.ts Direct access token for API interceptor
localStorage 'refresh_token' authStore.ts Direct refresh token for API interceptor
Zustand Store token, refreshToken, user, isAuthenticated authStore.ts In-memory auth state

Session Integrity Validation

Validation Storage Key Checked By Action on Mismatch
Account ID match _account_id in session AccountContextMiddleware:55-66 Force logout
User ID match _user_id in session AccountContextMiddleware:70-81 Force logout

Logout Flow Diagrams

Backend Session Contamination Logout

Request arrives
    ↓
AuthenticationMiddleware sets request.user
    ↓
AccountContextMiddleware.process_request()
    ↓
Check if user.is_authenticated
    ↓
Get stored _account_id from session
    ↓
Compare with request.account.id
    ↓
Mismatch detected? 
    ├─ Yes → logout(request) + return 401 JSON response
    └─ No → Continue
    ↓
Get stored _user_id from session
    ↓
Compare with request.user.id
    ↓
Mismatch detected?
    ├─ Yes → logout(request) + return 401 JSON response
    └─ No → Continue

Backend Account/Plan Validation Logout

Request arrives
    ↓
AccountContextMiddleware.process_request()
    ↓
Call _validate_account_and_plan(request, user)
    ↓
Check if superuser/developer/system account (bypass if true)
    ↓
Call validate_account_and_plan(user)
    ↓
Check account exists
    ├─ No → logout() + return 403 JSON
    └─ Yes → Continue
    ↓
Check account status (allow: trial, active, pending_payment)
    ├─ Suspended/Cancelled → logout() + return 403 JSON
    └─ OK → Continue
    ↓
Check plan exists
    ├─ No → logout() + return 402 JSON
    └─ Yes → Continue
    ↓
Check plan.is_active
    ├─ False → logout() + return 402 JSON
    └─ True → Continue

Frontend 401 Token Refresh Flow

API Request returns 401
    ↓
Check if refresh token exists
    ├─ No → logout() + redirect to /signin
    └─ Yes → Continue
    ↓
POST /v1/auth/refresh/ with refresh token
    ↓
Refresh successful?
    ├─ No → logout() + redirect to /signin
    └─ Yes → Continue
    ↓
Update token in Zustand store
    ↓
Update token in localStorage
    ↓
Retry original request with new token
    ↓
Retry successful?
    ├─ Yes → Return response to caller
    └─ No → Throw error (don't logout on retry failure)

Manual Logout Flow

User clicks logout button
    ↓
authStore.logout() called
    ↓
Clear all cookies (parse document.cookie and delete all)
    ↓
Clear localStorage items:
    - 'auth-storage'
    - 'access_token'
    - 'refresh_token'
    - 'site-storage'
    - 'sector-storage'
    ↓
Clear sessionStorage
    ↓
Reset Zustand stores:
    - authStore (user, token, refreshToken, isAuthenticated)
    - useSiteStore
    - useSectorStore
    - useBillingStore
    ↓
Redirect to /signin

Special Cases & Bypass Rules

Users Who Bypass Account/Plan Validation

  1. Superusers (user.is_superuser == True)

    • Bypass all account/plan checks
    • Never logged out for account/plan issues
    • Have full system access
  2. Developers (user.role == 'developer')

    • Bypass all account/plan checks
    • Have full system access for development/debugging
    • Never logged out for account/plan issues
  3. System Account Users (user.is_system_account_user() == True)

    • Bypass all account/plan checks
    • Used for internal/automated processes
    • Never logged out for account/plan issues

Throttling Bypass (Rate Limiting - CURRENTLY DISABLED)

IMPORTANT: Rate limiting is currently disabled. The DebugScopedRateThrottle.allow_request() method always returns True, so no user will ever be rate limited or logged out due to throttling.

When enabled, the following users would bypass throttling:

  1. Superusers
  2. Developers
  3. System account users
  4. DEBUG mode enabled
  5. IGNY8_DEBUG_THROTTLE environment variable set to True

Endpoints That Skip Account Validation

The following URL paths bypass AccountContextMiddleware entirely:

  1. /admin/* - Django admin panel
  2. /api/v1/auth/* - Authentication endpoints (login, register, refresh, etc.)

Public endpoints (no authentication required):

  • GET /api/v1/system/ping/ - Health check
  • POST /api/v1/auth/login/ - User login
  • POST /api/v1/auth/register/ - User registration
  • GET /api/v1/auth/plans/ - List subscription plans
  • GET /api/v1/auth/industries/ - List industries
  • GET /api/v1/system/status/ - System status
  • POST /api/v1/auth/refresh/ - Refresh access token

Debugging & Logging

Backend Logging

All automatic logouts are logged with the [AUTO-LOGOUT] prefix:

# Logger name: 'auth.middleware'
# Location: backend/igny8_core/auth/middleware.py

# Session contamination - account ID mismatch
logger.warning(
    f"[AUTO-LOGOUT] Session contamination: account_id mismatch. "
    f"Session={stored_account_id}, Current={request.account.id}, "
    f"User={request.user.id}, Path={request.path}, IP={request.META.get('REMOTE_ADDR')}"
)

# Session contamination - user ID mismatch
logger.warning(
    f"[AUTO-LOGOUT] Session contamination: user_id mismatch. "
    f"Session={stored_user_id}, Current={request.user.id}, "
    f"Account={request.account.id if request.account else None}, "
    f"Path={request.path}, IP={request.META.get('REMOTE_ADDR')}"
)

# Account/plan validation failed
logger.warning(
    f"[AUTO-LOGOUT] Account/plan validation failed: {error}. "
    f"User={request.user.id}, Account={getattr(request, 'account', None)}, "
    f"Path={request.path}, IP={request.META.get('REMOTE_ADDR')}"
)

Viewing Logout Logs

# View all auto-logout events
docker logs igny8_backend 2>&1 | grep "\[AUTO-LOGOUT\]"

# View session contamination events
docker logs igny8_backend 2>&1 | grep "Session contamination"

# View account/plan validation failures
docker logs igny8_backend 2>&1 | grep "Account/plan validation failed"

# Real-time monitoring
docker logs -f igny8_backend | grep "\[AUTO-LOGOUT\]"

Frontend Console Logs

The frontend logs all authentication-related events:

  • Token refresh attempts: "Attempting to refresh token..."
  • Token refresh success: "Token refreshed successfully"
  • Token refresh failure: "Token refresh failed:"
  • Logout triggered: "Logging out due to:"
  • Authentication errors: "Authentication error:"

Summary Statistics

  • Total Confirmed Logout Causes: 42
  • Backend Automatic: 17
  • Frontend Triggered: 14
  • Security & Policy: 5
  • Error & Exception: 6
  • Rate Limiting (Disabled): 1 (not currently active)


Maintenance Notes

Last Code Analysis: December 15, 2025
Version: 1.0
Status: Complete and Verified

This document was created through complete analysis of:

  • Backend settings and middleware
  • Authentication classes and utilities
  • Frontend auth store and API interceptor
  • Session and JWT token handling
  • Error handlers and exception flows
  • Container lifecycle and debugging logs

All causes listed are confirmed to exist in the current codebase and are accurate as of the analysis date.