From d607803e86954104eedc09a7d05f220803ce2c44 Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Mon, 27 Jul 2026 02:23:25 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20WebSocket=20403=20=E2=80=94=20SameSite?= =?UTF-8?q?=3DStrict=20blocked=20session=20cookie=20on=20WS=20connections?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- app/config.py | 2 +- app/plugins/builtins/kommunikation/routes.py | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/app/config.py b/app/config.py index 5b6487b..e41cf8d 100644 --- a/app/config.py +++ b/app/config.py @@ -36,7 +36,7 @@ class Settings(BaseSettings): bcrypt_rounds: int = 12 session_cookie_name: str = "leocrm_session" 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 password_reset_expiry_hours: int = 1 diff --git a/app/plugins/builtins/kommunikation/routes.py b/app/plugins/builtins/kommunikation/routes.py index 276c07c..8d1cd95 100644 --- a/app/plugins/builtins/kommunikation/routes.py +++ b/app/plugins/builtins/kommunikation/routes.py @@ -471,33 +471,23 @@ async def websocket_endpoint( 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 from app.config import get_settings from app.core.auth import get_session_data, get_redis, verify_ws_origin settings = get_settings() - _logger.warning("WS verify_ws_origin result: %s", 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") return session_id = websocket.cookies.get(settings.session_cookie_name) - _logger.warning("WS session_id: %s", session_id is not None) if not session_id: - _logger.warning("WS REJECTED: no session cookie") await websocket.close(code=4001, reason="Not authenticated") return redis = get_redis() 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: - _logger.warning("WS REJECTED: session expired") await websocket.close(code=4001, reason="Session expired") return @@ -507,9 +497,7 @@ async def websocket_endpoint( # Get WebSocket manager from service container from app.core.service_container import get_container container = get_container() - _logger.warning("WS container has comm_websocket: %s", 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") return