diff --git a/navi/api/websocket.py b/navi/api/websocket.py index eef286d..c9cd3c6 100644 --- a/navi/api/websocket.py +++ b/navi/api/websocket.py @@ -205,19 +205,23 @@ log.info("ws.session_found", session_id=session_id, session_user_id=session.user_id) - if user is not None: - try: - check_session_access(session, user) - log.info("ws.access_granted", session_id=session_id, user_id=user.id) - except Exception: - log.warning("ws.access_denied", session_id=session_id, user_id=user.id) - await websocket.close(code=4003, reason="Access denied") - return - else: - log.info("ws.anonymous_user", session_id=session_id) - + # Accept the WebSocket before checking access so that auth failures can be + # sent as WebSocket close codes rather than HTTP 403 on the upgrade request. await websocket.accept() - log.info("ws.connected", session_id=session_id) + log.info("ws.accepted", session_id=session_id) + + if user is None: + log.warning("ws.anonymous_denied", session_id=session_id) + await websocket.close(code=4003, reason="Authentication required") + return + + try: + check_session_access(session, user) + log.info("ws.access_granted", session_id=session_id, user_id=user.id) + except Exception: + log.warning("ws.access_denied", session_id=session_id, user_id=user.id) + await websocket.close(code=4003, reason="Access denied") + return from navi.api.deps import get_memory_store, get_registries, get_workers tools, profiles, backends, cp_registry = get_registries()