diff --git a/pkgs/servers/home-assistant/default.nix b/pkgs/servers/home-assistant/default.nix index 7b168d613d2a..8934ce242ed0 100644 --- a/pkgs/servers/home-assistant/default.nix +++ b/pkgs/servers/home-assistant/default.nix @@ -333,6 +333,9 @@ python3Packages.buildPythonApplication rec { url = "https://github.com/home-assistant/core/commit/e796d9c46744097585bfada483108a55ae16344a.patch"; hash = "sha256-T0Nb6LcL/21WdUm8RmczhHaVX92n5O/rpMdpqDVQ2VU="; }) + + # https://github.com/home-assistant/core/pull/172893 + ./patches/pyjwt-2.13-compat.patch ]; postPatch = '' diff --git a/pkgs/servers/home-assistant/patches/pyjwt-2.13-compat.patch b/pkgs/servers/home-assistant/patches/pyjwt-2.13-compat.patch new file mode 100644 index 000000000000..ad71ac997791 --- /dev/null +++ b/pkgs/servers/home-assistant/patches/pyjwt-2.13-compat.patch @@ -0,0 +1,48 @@ +diff --git a/homeassistant/auth/__init__.py b/homeassistant/auth/__init__.py +index e16c29ceaa8..8224aca0e43 100644 +--- a/homeassistant/auth/__init__.py ++++ b/homeassistant/auth/__init__.py +@@ -656,6 +656,8 @@ class AuthManager: + try: + unverif_claims = jwt_wrapper.unverified_hs256_token_decode(token) + except jwt.InvalidTokenError: ++ # PyJWT 2.13 raises InvalidKeyError (not an InvalidTokenError) when ++ # the refresh token's key has been removed and is therefore empty. + return None + + refresh_token = self.async_get_refresh_token( +@@ -673,7 +675,7 @@ class AuthManager: + jwt_wrapper.verify_and_decode( + token, jwt_key, leeway=10, issuer=issuer, algorithms=["HS256"] + ) +- except jwt.InvalidTokenError: ++ except jwt.InvalidTokenError, jwt.InvalidKeyError: + return None + + if refresh_token is None or not refresh_token.user.is_active: +diff --git a/homeassistant/components/html5/notify.py b/homeassistant/components/html5/notify.py +index c3ea03d01b9..98878696139 100644 +--- a/homeassistant/components/html5/notify.py ++++ b/homeassistant/components/html5/notify.py +@@ -327,7 +327,7 @@ class HTML5PushCallbackView(HomeAssistantView): + if target_check.get(ATTR_TARGET) in self.registrations: + possible_target = self.registrations[target_check[ATTR_TARGET]] + key = possible_target["subscription"]["keys"]["auth"] +- with suppress(jwt.exceptions.DecodeError): ++ with suppress(jwt.exceptions.DecodeError, jwt.exceptions.InvalidKeyError): + return jwt.decode(token, key, algorithms=["ES256", "HS256"]) + + return self.json_message( +diff --git a/tests/components/elmax/conftest.py b/tests/components/elmax/conftest.py +index 02f01036996..c9c3f13e9e3 100644 +--- a/tests/components/elmax/conftest.py ++++ b/tests/components/elmax/conftest.py +@@ -82,7 +82,7 @@ def httpx_mock_direct_fixture(base_uri: str) -> Generator[respx.MockRouter]: + expiration = datetime.now() + timedelta(hours=1) + decoded_jwt["payload"]["exp"] = int(expiration.timestamp()) + jws_string = jwt.encode( +- payload=decoded_jwt["payload"], algorithm="HS256", key="" ++ payload=decoded_jwt["payload"], algorithm="HS256", key="test" + ) + login_json["token"] = f"JWT {jws_string}" + login_route.return_value = Response(200, json=login_json)