From fc52b78c8420ef078cd9b468419d030f5022dcb7 Mon Sep 17 00:00:00 2001 From: Matt Dennis Date: Tue, 20 Oct 2026 16:13:41 +0100 Subject: [PATCH 1/2] fix: record token_created after device login device_login() returned the AuthenticationResult without storing it, so auth.token_created stayed None and get_device_data() reported no issue time after a device login. The session then fell back to the epoch default for token_created and treated every start as an expired token (issue #123). login() and sms_2fa() already go through _store_auth_result(); device_login() now does the same. Verified live: after a device login, start_session with the four-value device_data from get_device_data() now starts with the real expiry and performs no startup refresh. Co-Authored-By: Claude Fable 5.1 --- src/api/hive_auth_async.py | 5 +++++ tests/unit/test_hive_auth_async.py | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/api/hive_auth_async.py b/src/api/hive_auth_async.py index 378e7a9a..63da0b5b 100644 --- a/src/api/hive_auth_async.py +++ b/src/api/hive_auth_async.py @@ -413,6 +413,11 @@ async def device_login(self): _LOGGER.error("Device login failed: cannot reach endpoint.") raise HiveApiError from err + if result and "AuthenticationResult" in result: + # Record the access token and issue time so get_device_data() reports + # when these tokens were created, not None, after a device login. + self._store_auth_result(result) + _LOGGER.debug("device_login - Device authentication completed successfully.") return result diff --git a/tests/unit/test_hive_auth_async.py b/tests/unit/test_hive_auth_async.py index 5a0114b9..adade9dc 100644 --- a/tests/unit/test_hive_auth_async.py +++ b/tests/unit/test_hive_auth_async.py @@ -2,6 +2,7 @@ from __future__ import annotations +import datetime from unittest.mock import AsyncMock, MagicMock, patch import botocore.exceptions @@ -1230,6 +1231,32 @@ async def test_successful_device_login_returns_auth_result(self): mock_pdc.assert_called_once_with(initial_result["ChallengeParameters"]) assert result is final_result + async def test_device_login_records_access_token_and_token_created(self): + """device_login() must store the tokens like login() and sms_2fa() do. + + Otherwise get_device_data() reports token_created=None after a device + login and the session falls back to the epoch default, so every start + looks like an expired token (issue #123). + """ + auth = await _make_auth(device_key="dk-abc", device_group_key="grp-abc") + auth.device_password = "dev-pass" # pragma: allowlist secret + assert auth.token_created is None + + initial_result = {"ChallengeParameters": {"USERNAME": "user@test.com"}} + final_result = {"AuthenticationResult": {"AccessToken": "device-access-token"}} + + before = datetime.datetime.now() + with patch.object(auth, "process_device_challenge", new_callable=AsyncMock): + auth.loop.run_in_executor = AsyncMock( + side_effect=[initial_result, final_result] + ) + await auth.device_login() + + assert auth.access_token == "device-access-token" + assert isinstance(auth.token_created, datetime.datetime) + assert auth.token_created >= before + assert (await auth.get_device_data())[3] == auth.token_created + async def test_device_login_calls_second_respond_to_auth_challenge(self): """Lines 367-375: second respond_to_auth_challenge is called with device challenge.""" auth = await _make_auth(device_key="dk-xyz", device_group_key="grp-xyz") From cd878ffb5d9785abbb74b6eecd74f93e8ed9bcca Mon Sep 17 00:00:00 2001 From: Matt Dennis Date: Tue, 20 Oct 2026 16:16:09 +0100 Subject: [PATCH 2/2] chore: update detect-secrets baseline line numbers Co-Authored-By: Claude Fable 5.1 --- .secrets.baseline | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 83a918e4..031bb917 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -486,14 +486,14 @@ "filename": "tests/unit/test_hive_auth_async.py", "hashed_secret": "5c5a15a8b0b3e154d77746945e563ba40100681b", "is_verified": false, - "line_number": 174 + "line_number": 175 }, { "type": "Secret Keyword", "filename": "tests/unit/test_hive_auth_async.py", "hashed_secret": "d8bce9746547bb7743e5933fbf0fc4f2d2cbcad3", "is_verified": false, - "line_number": 273 + "line_number": 274 } ], "tests/unit/test_hive_auth_async_extended.py": [ @@ -580,5 +580,5 @@ } ] }, - "generated_at": "2026-09-26T17:01:30Z" + "generated_at": "2026-10-20T15:16:01Z" }