fix: WebSocket 403 — SameSite=Strict blocked session cookie on WS connections
Check Cross-Plugin Imports / check (push) Has been cancelled

Root cause: session_cookie_samesite was 'strict' which prevents the
browser from sending the session cookie on WebSocket upgrade requests.
Changed to 'lax' which allows WebSocket cookies while still blocking
cross-site POST CSRF attacks.

Also removed debug logging from kommunikation routes.
This commit is contained in:
Agent Zero
2026-07-27 02:23:25 +02:00
parent 35a9ce1e7b
commit d607803e86
2 changed files with 1 additions and 13 deletions
+1 -1
View File
@@ -36,7 +36,7 @@ class Settings(BaseSettings):
bcrypt_rounds: int = 12 bcrypt_rounds: int = 12
session_cookie_name: str = "leocrm_session" session_cookie_name: str = "leocrm_session"
session_cookie_secure: bool = True # Secure by default — set to False only for local HTTP development session_cookie_secure: bool = True # Secure by default — set to False only for local HTTP development
session_cookie_samesite: str = "strict" session_cookie_samesite: str = "lax" # Lax allows WebSocket cookies; Strict blocks them
session_cookie_httponly: bool = True session_cookie_httponly: bool = True
password_reset_expiry_hours: int = 1 password_reset_expiry_hours: int = 1
@@ -471,33 +471,23 @@ async def websocket_endpoint(
Authenticates via session cookie. On connect, subscribes user to all their conversations. Authenticates via session cookie. On connect, subscribes user to all their conversations.
""" """
import logging as _log
_logger = _log.getLogger("websocket_debug")
_logger.warning("WS CONNECT START: path=%s headers=%s", websocket.url.path, dict(websocket.headers))
# Verify Origin header against allowed CORS origins # Verify Origin header against allowed CORS origins
from app.config import get_settings from app.config import get_settings
from app.core.auth import get_session_data, get_redis, verify_ws_origin from app.core.auth import get_session_data, get_redis, verify_ws_origin
settings = get_settings() settings = get_settings()
_logger.warning("WS verify_ws_origin result: %s", verify_ws_origin(websocket))
if not verify_ws_origin(websocket): if not verify_ws_origin(websocket):
_logger.warning("WS REJECTED: origin not allowed")
await websocket.close(code=4003, reason="Origin not allowed") await websocket.close(code=4003, reason="Origin not allowed")
return return
session_id = websocket.cookies.get(settings.session_cookie_name) session_id = websocket.cookies.get(settings.session_cookie_name)
_logger.warning("WS session_id: %s", session_id is not None)
if not session_id: if not session_id:
_logger.warning("WS REJECTED: no session cookie")
await websocket.close(code=4001, reason="Not authenticated") await websocket.close(code=4001, reason="Not authenticated")
return return
redis = get_redis() redis = get_redis()
session_data = await get_session_data(redis, session_id) session_data = await get_session_data(redis, session_id)
_logger.warning("WS session_data: %s", session_data is not None)
if session_data is None: if session_data is None:
_logger.warning("WS REJECTED: session expired")
await websocket.close(code=4001, reason="Session expired") await websocket.close(code=4001, reason="Session expired")
return return
@@ -507,9 +497,7 @@ async def websocket_endpoint(
# Get WebSocket manager from service container # Get WebSocket manager from service container
from app.core.service_container import get_container from app.core.service_container import get_container
container = get_container() container = get_container()
_logger.warning("WS container has comm_websocket: %s", container.has("comm_websocket"))
if not container.has("comm_websocket"): if not container.has("comm_websocket"):
_logger.warning("WS REJECTED: comm_websocket not in container. Services: %s", list(container._services.keys()))
await websocket.close(code=4003, reason="Messaging not available") await websocket.close(code=4003, reason="Messaging not available")
return return