final logout related fixes and cookies and session

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-16 19:16:50 +00:00
parent 1887f2a665
commit 84fd4bc11a
5 changed files with 175 additions and 13 deletions

View File

@@ -746,7 +746,7 @@ class User(AbstractUser):
if not self.account:
return Site.objects.none()
base_sites = Site.objects.filter(account=self.account, is_active=True)
base_sites = Site.objects.filter(account=self.account)
if self.role in ['owner', 'admin', 'developer'] or self.is_superuser or self.is_system_account_user():
return base_sites

View File

@@ -46,14 +46,56 @@ class RegisterView(APIView):
permission_classes = [permissions.AllowAny]
def post(self, request):
from .utils import generate_access_token, generate_refresh_token, get_token_expiry
from django.contrib.auth import login
from .utils import generate_access_token, generate_refresh_token, get_access_token_expiry, get_refresh_token_expiry
from django.contrib.auth import login, logout
from django.utils import timezone
force_logout = request.data.get('force_logout', False)
serializer = RegisterSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
# SECURITY: Check for session contamination before login
# If there's an existing session from a different user, handle it
if request.session.session_key:
existing_user_id = request.session.get('_auth_user_id')
if existing_user_id and str(existing_user_id) != str(user.id):
# Get existing user details
try:
existing_user = User.objects.get(id=existing_user_id)
existing_email = existing_user.email
existing_username = existing_user.username or existing_email.split('@')[0]
except User.DoesNotExist:
existing_email = 'Unknown user'
existing_username = 'Unknown'
# If not forcing logout, return conflict info
if not force_logout:
return Response(
{
'status': 'error',
'error': 'session_conflict',
'message': f'You have an active session for another account ({existing_email}). Please logout first or choose to continue.',
'existing_user': {
'email': existing_email,
'username': existing_username,
'id': existing_user_id
},
'requested_user': {
'email': user.email,
'username': user.username or user.email.split('@')[0],
'id': user.id
}
},
status=status.HTTP_409_CONFLICT
)
# Force logout - clean existing session completely
logout(request)
# Clear all session data
request.session.flush()
# Log the user in (create session for session authentication)
login(request, user)
@@ -63,8 +105,8 @@ class RegisterView(APIView):
# Generate JWT tokens
access_token = generate_access_token(user, account)
refresh_token = generate_refresh_token(user, account)
access_expires_at = timezone.now() + get_token_expiry('access')
refresh_expires_at = timezone.now() + get_token_expiry('refresh')
access_expires_at = timezone.now() + get_access_token_expiry()
refresh_expires_at = timezone.now() + get_refresh_token_expiry()
user_serializer = UserSerializer(user)
return success_response(
@@ -104,6 +146,7 @@ class LoginView(APIView):
email = serializer.validated_data['email']
password = serializer.validated_data['password']
remember_me = serializer.validated_data.get('remember_me', False)
force_logout = request.data.get('force_logout', False)
try:
user = User.objects.select_related('account', 'account__plan').get(email=email)
@@ -115,6 +158,47 @@ class LoginView(APIView):
)
if user.check_password(password):
# SECURITY: Check for session contamination before login
# If user has a session cookie from a different user, handle it
if request.session.session_key:
existing_user_id = request.session.get('_auth_user_id')
if existing_user_id and str(existing_user_id) != str(user.id):
# Get existing user details
try:
existing_user = User.objects.get(id=existing_user_id)
existing_email = existing_user.email
existing_username = existing_user.username or existing_email.split('@')[0]
except User.DoesNotExist:
existing_email = 'Unknown user'
existing_username = 'Unknown'
# If not forcing logout, return conflict info
if not force_logout:
return Response(
{
'status': 'error',
'error': 'session_conflict',
'message': f'You have an active session for another account ({existing_email}). Please logout first or choose to continue.',
'existing_user': {
'email': existing_email,
'username': existing_username,
'id': existing_user_id
},
'requested_user': {
'email': user.email,
'username': user.username or user.email.split('@')[0],
'id': user.id
}
},
status=status.HTTP_409_CONFLICT
)
# Force logout - clean existing session completely
from django.contrib.auth import logout
logout(request)
# Clear all session data
request.session.flush()
# Log the user in (create session for session authentication)
from django.contrib.auth import login
login(request, user)
@@ -123,12 +207,12 @@ class LoginView(APIView):
account = getattr(user, 'account', None)
# Generate JWT tokens
from .utils import generate_access_token, generate_refresh_token, get_access_token_expiry, get_token_expiry
from .utils import generate_access_token, generate_refresh_token, get_access_token_expiry, get_refresh_token_expiry
from django.utils import timezone
access_token = generate_access_token(user, account, remember_me=remember_me)
refresh_token = generate_refresh_token(user, account)
access_expires_at = timezone.now() + get_access_token_expiry(remember_me=remember_me)
refresh_expires_at = timezone.now() + get_token_expiry('refresh')
refresh_expires_at = timezone.now() + get_refresh_token_expiry()
# Serialize user data safely, handling missing account relationship
try:
@@ -274,6 +358,7 @@ class RefreshTokenView(APIView):
account = getattr(user, 'account', None)
# Generate new access token
from .utils import get_token_expiry
access_token = generate_access_token(user, account)
access_expires_at = get_token_expiry('access')

View File

@@ -92,15 +92,17 @@ CSRF_TRUSTED_ORIGINS = [
USE_SECURE_COOKIES = os.getenv('USE_SECURE_COOKIES', 'False').lower() == 'true'
SESSION_COOKIE_SECURE = USE_SECURE_COOKIES
CSRF_COOKIE_SECURE = USE_SECURE_COOKIES
CSRF_COOKIE_SAMESITE = 'Lax' # Match session cookie setting
CSRF_COOKIE_DOMAIN = '.igny8.com' # Share CSRF cookie across subdomains
# CRITICAL: Session isolation to prevent contamination
SESSION_COOKIE_NAME = 'igny8_sessionid' # Custom name to avoid conflicts
SESSION_COOKIE_HTTPONLY = True # Prevent JavaScript access
SESSION_COOKIE_SAMESITE = 'Strict' # Prevent cross-site cookie sharing
SESSION_COOKIE_SAMESITE = 'Lax' # Changed from Strict to Lax - allows same-site top-level navigation
SESSION_COOKIE_AGE = 3600 # 1 hour - extends on every request due to SESSION_SAVE_EVERY_REQUEST
SESSION_SAVE_EVERY_REQUEST = True # CRITICAL: Update session on every request to prevent idle timeout
SESSION_COOKIE_PATH = '/' # Explicit path
# Don't set SESSION_COOKIE_DOMAIN - let it default to current domain for strict isolation
SESSION_COOKIE_DOMAIN = '.igny8.com' # CRITICAL: Share cookie across subdomains (app.igny8.com and api.igny8.com)
# CRITICAL: Custom authentication backend to disable user caching
AUTHENTICATION_BACKENDS = [