From f116cbbfc3a112bc9eb164400a9d9de22d73810f Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 11 Mar 2026 17:52:37 +0100 Subject: [PATCH] Support unix socket-authenticated WebSocket connections --- supervisor/homeassistant/api.py | 11 +++++---- supervisor/homeassistant/websocket.py | 32 +++++++++++++++++++-------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/supervisor/homeassistant/api.py b/supervisor/homeassistant/api.py index f42db8356..43056def5 100644 --- a/supervisor/homeassistant/api.py +++ b/supervisor/homeassistant/api.py @@ -193,10 +193,13 @@ class HomeAssistantAPI(CoreSysAttributes): if content_type is not None: headers[hdrs.CONTENT_TYPE] = content_type + use_unix = self.use_unix_socket + for _ in (1, 2): try: - await self.ensure_access_token() - headers[hdrs.AUTHORIZATION] = f"Bearer {self.access_token}" + if not use_unix: + await self.ensure_access_token() + headers[hdrs.AUTHORIZATION] = f"Bearer {self.access_token}" async with self._session.request( method, url, @@ -207,8 +210,8 @@ class HomeAssistantAPI(CoreSysAttributes): params=params, ssl=False, ) as resp: - # Access token expired - if resp.status == 401: + # Access token expired (only relevant for TCP) + if resp.status == 401 and not use_unix: self.access_token = None continue yield resp diff --git a/supervisor/homeassistant/websocket.py b/supervisor/homeassistant/websocket.py index 86daf4d0f..5ea3204cc 100644 --- a/supervisor/homeassistant/websocket.py +++ b/supervisor/homeassistant/websocket.py @@ -140,16 +140,28 @@ class WSClient: @classmethod async def connect_with_auth( - cls, session: aiohttp.ClientSession, url: str, token: str + cls, + session: aiohttp.ClientSession, + url: str, + token: str | None, ) -> WSClient: - """Create an authenticated websocket client.""" + """Create an authenticated websocket client. + + When token is None (Unix socket), Core sends auth_ok immediately + without requiring an auth exchange. + """ try: client = await session.ws_connect(url, ssl=False) except aiohttp.client_exceptions.ClientConnectorError: raise HomeAssistantWSConnectionError("Can't connect") from None - hello_message = await client.receive_json() + first_message = await client.receive_json() + if first_message[ATTR_TYPE] == "auth_ok": + # Unix socket: Core already authenticated us + return cls(AwesomeVersion(first_message["ha_version"]), client) + + # TCP: auth_required → send token → auth_ok await client.send_json( {ATTR_TYPE: WSType.AUTH, ATTR_ACCESS_TOKEN: token}, dumps=json_dumps ) @@ -159,7 +171,7 @@ class WSClient: if auth_ok_message[ATTR_TYPE] != "auth_ok": raise HomeAssistantAPIError("AUTH NOT OK") - return cls(AwesomeVersion(hello_message["ha_version"]), client) + return cls(AwesomeVersion(first_message["ha_version"]), client) class HomeAssistantWebSocket(CoreSysAttributes): @@ -186,12 +198,14 @@ class HomeAssistantWebSocket(CoreSysAttributes): if self._client is not None and self._client.connected: return self._client - with suppress(asyncio.TimeoutError, aiohttp.ClientError): - await self.sys_homeassistant.api.ensure_access_token() + api = self.sys_homeassistant.api + if not api.use_unix_socket: + with suppress(asyncio.TimeoutError, aiohttp.ClientError): + await api.ensure_access_token() client = await WSClient.connect_with_auth( - self.sys_homeassistant.api._session, - self.sys_homeassistant.api._ws_url, - cast(str, self.sys_homeassistant.api.access_token), + api._session, + api._ws_url, + None if api.use_unix_socket else cast(str, api.access_token), ) self.sys_create_task(client.start_listener())