diff --git a/navi/api/websocket.py b/navi/api/websocket.py index 7b24e7d..82a40cd 100644 --- a/navi/api/websocket.py +++ b/navi/api/websocket.py @@ -192,9 +192,17 @@ async def websocket_session( session_id: str, websocket: WebSocket, - user: Annotated[User | None, Depends(get_current_user_ws)] = None, ) -> None: - log.info("ws.handler_enter", session_id=session_id, user_id=user.id if user else None) + log.info("ws.handler_enter", session_id=session_id) + + # Resolve user manually — bypass FastAPI Depends() to avoid HTTP 403 + # on the upgrade request when auth resolution fails. + try: + user: User | None = await get_current_user_ws(websocket) + except Exception as exc: + log.warning("ws.resolve_user_exc", session_id=session_id, exc_type=type(exc).__name__, error=str(exc)) + user = None + log.info("ws.user_resolved", session_id=session_id, user_id=user.id if user else None) session_store = get_session_store() diff --git a/navi/auth/deps.py b/navi/auth/deps.py index 477c4fd..f485008 100644 --- a/navi/auth/deps.py +++ b/navi/auth/deps.py @@ -140,10 +140,7 @@ async def get_current_user_ws(websocket) -> User | None: """Resolve the current user from the auth session cookie for WebSocket connections.""" - log.info("auth.ws_dep_enter") - result = await _resolve_user(websocket) - log.info("auth.ws_dep_exit", user_id=result.id if result else None) - return result + return await _resolve_user(websocket) # ── Helpers that talk to the session store ────────────────────────────────── diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 03cf7b9..7c53a5a 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -69,6 +69,13 @@ from navi.auth.deps import get_current_user_ws app.dependency_overrides[get_current_user] = lambda: fake_user + # get_current_user_ws is called directly inside websocket_session (no Depends), + # so we monkeypatch the source module. Keep dependency_overrides for HTTP + # endpoints that still use Depends(get_current_user_ws). + async def _fake_get_user_ws(websocket): + return fake_user + monkeypatch.setattr("navi.auth.deps.get_current_user_ws", _fake_get_user_ws) + monkeypatch.setattr("navi.api.deps.get_current_user_ws", _fake_get_user_ws) app.dependency_overrides[get_current_user_ws] = lambda: fake_user app.dependency_overrides[require_user] = lambda: fake_user app.dependency_overrides[require_admin] = lambda: fake_user