messy logout fixing

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-15 12:01:41 +00:00
parent 06e5f252a4
commit 4fb3a144d7
27 changed files with 4396 additions and 95 deletions

View File

@@ -5,7 +5,6 @@ Extracts account from JWT token and injects into request context
import logging
from django.utils.deprecation import MiddlewareMixin
from django.http import JsonResponse
from django.contrib.auth import logout
from rest_framework import status
logger = logging.getLogger('auth.middleware')
@@ -41,45 +40,19 @@ class AccountContextMiddleware(MiddlewareMixin):
# Validate account/plan - but use the user object already set by Django
validation_error = self._validate_account_and_plan(request, request.user)
if validation_error:
# CRITICAL: Return error response, DO NOT logout
# Frontend will handle auth errors appropriately
return validation_error
# Set request.account from the user's account relationship
# This is already loaded, no need to query DB again
request.account = getattr(request.user, 'account', None)
# CRITICAL: Add account ID to session to prevent cross-contamination
# This ensures each session is tied to a specific account
# Store account and user IDs in session for audit purposes only
# DO NOT use these for validation - they are informational only
if request.account:
request.session['_account_id'] = request.account.id
request.session['_user_id'] = request.user.id
# Verify session integrity - if stored IDs don't match, logout
stored_account_id = request.session.get('_account_id')
stored_user_id = request.session.get('_user_id')
if stored_account_id and stored_account_id != request.account.id:
# Session contamination detected - force logout
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')}"
)
logout(request)
return JsonResponse(
{'success': False, 'error': 'Session integrity violation detected. Please login again.'},
status=status.HTTP_401_UNAUTHORIZED
)
if stored_user_id and stored_user_id != request.user.id:
# Session contamination detected - force logout
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')}"
)
logout(request)
return JsonResponse(
{'success': False, 'error': 'Session integrity violation detected. Please login again.'},
status=status.HTTP_401_UNAUTHORIZED
)
return None
except (AttributeError, Exception):
@@ -128,6 +101,7 @@ class AccountContextMiddleware(MiddlewareMixin):
user = User.objects.select_related('account', 'account__plan').get(id=user_id)
validation_error = self._validate_account_and_plan(request, user)
if validation_error:
# CRITICAL: Return error response, DO NOT logout
return validation_error
if account_id:
# Verify account still exists
@@ -184,18 +158,17 @@ class AccountContextMiddleware(MiddlewareMixin):
return None
def _deny_request(self, request, error, status_code):
"""Logout session users (if any) and return a consistent JSON error."""
try:
if hasattr(request, 'user') and request.user and request.user.is_authenticated:
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')}"
)
logout(request)
except Exception as e:
logger.error(f"[AUTO-LOGOUT] Error during logout: {e}")
"""Return a consistent JSON error WITHOUT logging out the user."""
# Log the denial for audit purposes
logger.warning(
f"[ACCESS-DENIED] {error}. "
f"User={request.user.id if hasattr(request, 'user') and request.user else 'anonymous'}, "
f"Account={getattr(request, 'account', None)}, "
f"Path={request.path}, IP={request.META.get('REMOTE_ADDR')}"
)
# Return error response - frontend will handle appropriately
# DO NOT call logout() - let the frontend decide based on error type
return JsonResponse(
{
'success': False,