updates
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
"""
|
||||
User session management routes.
|
||||
"""
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, Response, Cookie
|
||||
from sqlalchemy.orm import Session
|
||||
from ...shared.config.database import get_db
|
||||
from ...shared.config.logging_config import get_logger
|
||||
from ...shared.config.settings import settings
|
||||
from ...security.middleware.auth import get_current_user
|
||||
from ...auth.models.user import User
|
||||
from ...auth.models.user_session import UserSession
|
||||
from ...auth.services.session_service import session_service
|
||||
from ...shared.utils.response_helpers import success_response
|
||||
from jose import jwt
|
||||
|
||||
logger = get_logger(__name__)
|
||||
router = APIRouter(prefix='/sessions', tags=['sessions'])
|
||||
@@ -44,13 +47,15 @@ async def get_my_sessions(
|
||||
@router.delete('/{session_id}')
|
||||
async def revoke_session(
|
||||
session_id: int,
|
||||
request: Request,
|
||||
response: Response,
|
||||
current_user: User = Depends(get_current_user),
|
||||
access_token: str = Cookie(None, alias='accessToken'),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Revoke a specific session."""
|
||||
try:
|
||||
# Verify session belongs to user
|
||||
from ...auth.models.user_session import UserSession
|
||||
session = db.query(UserSession).filter(
|
||||
UserSession.id == session_id,
|
||||
UserSession.user_id == current_user.id
|
||||
@@ -59,10 +64,62 @@ async def revoke_session(
|
||||
if not session:
|
||||
raise HTTPException(status_code=404, detail='Session not found')
|
||||
|
||||
# Check if this is the current session being revoked
|
||||
# We detect this by checking if:
|
||||
# 1. The session IP matches the request IP (if available)
|
||||
# 2. The session is the most recent active session
|
||||
is_current_session = False
|
||||
try:
|
||||
client_ip = request.client.host if request.client else None
|
||||
user_agent = request.headers.get('User-Agent', '')
|
||||
|
||||
# Check if session matches current request characteristics
|
||||
if client_ip and session.ip_address == client_ip:
|
||||
# Also check if it's the most recent session
|
||||
recent_session = db.query(UserSession).filter(
|
||||
UserSession.user_id == current_user.id,
|
||||
UserSession.is_active == True
|
||||
).order_by(UserSession.last_activity.desc()).first()
|
||||
|
||||
if recent_session and recent_session.id == session_id:
|
||||
is_current_session = True
|
||||
except Exception as e:
|
||||
logger.warning(f'Could not determine if session is current: {str(e)}')
|
||||
# If we can't determine, check if it's the only active session
|
||||
active_sessions_count = db.query(UserSession).filter(
|
||||
UserSession.user_id == current_user.id,
|
||||
UserSession.is_active == True
|
||||
).count()
|
||||
if active_sessions_count <= 1:
|
||||
is_current_session = True
|
||||
|
||||
success = session_service.revoke_session(db=db, session_token=session.session_token)
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail='Session not found')
|
||||
|
||||
# If this was the current session, clear cookies and indicate logout needed
|
||||
if is_current_session:
|
||||
from ...shared.config.settings import settings
|
||||
samesite_value = 'strict' if settings.is_production else 'lax'
|
||||
# Clear access token cookie
|
||||
response.delete_cookie(
|
||||
key='accessToken',
|
||||
path='/',
|
||||
samesite=samesite_value,
|
||||
secure=settings.is_production
|
||||
)
|
||||
# Clear refresh token cookie
|
||||
response.delete_cookie(
|
||||
key='refreshToken',
|
||||
path='/',
|
||||
samesite=samesite_value,
|
||||
secure=settings.is_production
|
||||
)
|
||||
return success_response(
|
||||
message='Session revoked successfully. You have been logged out.',
|
||||
data={'logout_required': True}
|
||||
)
|
||||
|
||||
return success_response(message='Session revoked successfully')
|
||||
except HTTPException:
|
||||
raise
|
||||
@@ -72,19 +129,41 @@ async def revoke_session(
|
||||
|
||||
@router.post('/revoke-all')
|
||||
async def revoke_all_sessions(
|
||||
request: Request,
|
||||
response: Response,
|
||||
current_user: User = Depends(get_current_user),
|
||||
access_token: str = Cookie(None, alias='accessToken'),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Revoke all sessions for current user."""
|
||||
try:
|
||||
count = session_service.revoke_all_user_sessions(
|
||||
db=db,
|
||||
user_id=current_user.id
|
||||
user_id=current_user.id,
|
||||
exclude_token=None # Don't exclude current session, revoke all
|
||||
)
|
||||
|
||||
# Clear cookies since all sessions (including current) are revoked
|
||||
from ...shared.config.settings import settings
|
||||
samesite_value = 'strict' if settings.is_production else 'lax'
|
||||
# Clear access token cookie
|
||||
response.delete_cookie(
|
||||
key='accessToken',
|
||||
path='/',
|
||||
samesite=samesite_value,
|
||||
secure=settings.is_production
|
||||
)
|
||||
# Clear refresh token cookie
|
||||
response.delete_cookie(
|
||||
key='refreshToken',
|
||||
path='/',
|
||||
samesite=samesite_value,
|
||||
secure=settings.is_production
|
||||
)
|
||||
|
||||
return success_response(
|
||||
data={'revoked_count': count},
|
||||
message=f'Revoked {count} session(s)'
|
||||
data={'revoked_count': count, 'logout_required': True},
|
||||
message=f'Revoked {count} session(s). You have been logged out.'
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f'Error revoking all sessions: {str(e)}', exc_info=True)
|
||||
|
||||
Reference in New Issue
Block a user