fix: Cookies now properly cleared on logout

This commit is contained in:
Ida Dittrich 2025-10-12 16:13:27 +02:00
parent dedee0ecda
commit 4e06f7e661
2 changed files with 48 additions and 8 deletions

View file

@ -14,7 +14,7 @@ from pydantic import BaseModel
# Import auth modules
from modules.security.auth import getCurrentUser, limiter, SECRET_KEY, ALGORITHM
from modules.security.jwtService import createAccessToken, createRefreshToken, setAccessTokenCookie, setRefreshTokenCookie
from modules.security.jwtService import createAccessToken, createRefreshToken, setAccessTokenCookie, setRefreshTokenCookie, clearAccessTokenCookie, clearRefreshTokenCookie
from modules.interfaces.interfaceDbAppObjects import getInterface, getRootInterface
from modules.datamodels.datamodelUam import User, UserInDB, AuthAuthority, UserPrivilege
from modules.datamodels.datamodelSecurity import Token
@ -365,15 +365,18 @@ async def logout(request: Request, response: Response, currentUser: User = Depen
# Don't fail if audit logging fails
pass
# Clear httpOnly cookies
response.delete_cookie(key="auth_token", httponly=True, samesite="strict")
response.delete_cookie(key="refresh_token", httponly=True, samesite="strict")
return JSONResponse({
# Create the JSON response first
json_response = JSONResponse({
"message": "Successfully logged out - cookies cleared",
"revokedTokens": revoked
})
# Clear httpOnly cookies on the response we're actually returning
clearAccessTokenCookie(json_response)
clearRefreshTokenCookie(json_response)
return json_response
except Exception as e:
logger.error(f"Error during logout: {str(e)}")
raise HTTPException(

View file

@ -17,6 +17,11 @@ ALGORITHM = APP_CONFIG.get("Auth_ALGORITHM")
ACCESS_TOKEN_EXPIRE_MINUTES = int(APP_CONFIG.get("APP_TOKEN_EXPIRY"))
REFRESH_TOKEN_EXPIRE_DAYS = int(APP_CONFIG.get("APP_REFRESH_TOKEN_EXPIRY", "7"))
# Cookie security settings - use secure cookies only in production (HTTPS)
# In development (HTTP), secure=True would prevent cookies from being set/cleared properly
ENV_TYPE = APP_CONFIG.get("APP_ENV_TYPE", "dev")
USE_SECURE_COOKIES = ENV_TYPE in ["prod", "production"]
def createAccessToken(data: dict, expiresDelta: Optional[timedelta] = None) -> Tuple[str, "datetime"]:
"""Create a JWT access token and return (token, expiresAt)."""
@ -52,8 +57,9 @@ def setAccessTokenCookie(response: Response, token: str, expiresDelta: Optional[
key="auth_token",
value=token,
httponly=True,
secure=True,
secure=USE_SECURE_COOKIES, # Only secure in production (HTTPS)
samesite="strict",
path="/",
max_age=maxAge
)
@ -64,9 +70,40 @@ def setRefreshTokenCookie(response: Response, token: str) -> None:
key="refresh_token",
value=token,
httponly=True,
secure=True,
secure=USE_SECURE_COOKIES, # Only secure in production (HTTPS)
samesite="strict",
path="/",
max_age=REFRESH_TOKEN_EXPIRE_DAYS * 24 * 60 * 60
)
def clearAccessTokenCookie(response: Response) -> None:
"""
Clear access token cookie by setting it to expire immediately.
Uses both raw header manipulation and FastAPI's delete_cookie for maximum browser compatibility.
"""
# Primary method: Raw Set-Cookie header for guaranteed deletion
response.headers.append(
"Set-Cookie",
f"auth_token=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=Strict"
)
# Fallback: Also use FastAPI's built-in method
response.delete_cookie(key="auth_token", path="/")
def clearRefreshTokenCookie(response: Response) -> None:
"""
Clear refresh token cookie by setting it to expire immediately.
Uses both raw header manipulation and FastAPI's delete_cookie for maximum browser compatibility.
"""
# Primary method: Raw Set-Cookie header for guaranteed deletion
response.headers.append(
"Set-Cookie",
f"refresh_token=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; SameSite=Strict"
)
# Fallback: Also use FastAPI's built-in method
response.delete_cookie(key="refresh_token", path="/")