Skip to content
This repository was archived by the owner on Jul 28, 2024. It is now read-only.

Commit 82b19c6

Browse files
committed
Fix restoring login from stored access token. (#10)
* Rename authentication token to proper access token * Bump matrix-nio to 0.21.1 to take advantage of whoami * Fix restoring login from stored access token. * Extra logging. * Bump matrix-nio to 0.21.2 for bugfixes.
1 parent 618e741 commit 82b19c6

File tree

5 files changed

+46
-29
lines changed

5 files changed

+46
-29
lines changed

custom_components/matrix/__init__.py

+27-16
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
LoginError,
2020
Response,
2121
UploadError,
22-
UploadResponse,
22+
UploadResponse, WhoamiError, WhoamiResponse,
2323
)
2424
import voluptuous as vol
2525

@@ -166,7 +166,7 @@ def __init__(
166166
self.hass = hass
167167

168168
self._session_filepath = config_file
169-
self._auth_tokens: JsonObjectType = {}
169+
self._access_tokens: JsonObjectType = {}
170170

171171
self._homeserver = homeserver
172172
self._verify_tls = verify_ssl
@@ -192,7 +192,7 @@ async def stop_client(event: HassEvent) -> None:
192192

193193
async def handle_startup(event: HassEvent) -> None:
194194
"""Run once when Home Assistant finished startup."""
195-
self._auth_tokens = await self._get_auth_tokens()
195+
self._access_tokens = await self._get_auth_tokens()
196196
await self._login()
197197
await self._join_rooms()
198198
# Sync once so that we don't respond to past events.
@@ -283,44 +283,55 @@ async def _join_rooms(self) -> None:
283283
await asyncio.wait(rooms)
284284

285285
async def _get_auth_tokens(self) -> JsonObjectType:
286-
"""Read sorted authentication tokens from disk."""
286+
"""Read sorted access tokens from disk."""
287287
try:
288288
return load_json_object(self._session_filepath)
289289
except HomeAssistantError as ex:
290290
_LOGGER.warning(
291-
"Loading authentication tokens from file '%s' failed: %s",
291+
"Loading access tokens from file '%s' failed: %s",
292292
self._session_filepath,
293293
str(ex),
294294
)
295295
return {}
296296

297297
async def _store_auth_token(self, token: str) -> None:
298-
"""Store authentication token to session and persistent storage."""
299-
self._auth_tokens[self._mx_id] = token
298+
"""Store access token to session and persistent storage."""
299+
self._access_tokens[self._mx_id] = token
300300

301301
await self.hass.async_add_executor_job(
302-
save_json, self._session_filepath, self._auth_tokens, True # private=True
302+
save_json, self._session_filepath, self._access_tokens, True # private=True
303303
)
304304

305305
async def _login(self) -> None:
306306
"""Log in to the Matrix homeserver.
307307
308-
Attempts to use the stored authentication token.
308+
Attempts to use the stored access token.
309309
If that fails, then tries using the password.
310310
If that also fails, raises LocalProtocolError.
311311
"""
312312

313-
# If we have an authentication token
314-
if (token := self._auth_tokens.get(self._mx_id)) is not None:
315-
response = await self._client.login(token=token)
316-
_LOGGER.debug("Logging in using stored token")
317-
318-
if isinstance(response, LoginError):
313+
# If we have an access token
314+
if (token := self._access_tokens.get(self._mx_id)) is not None:
315+
_LOGGER.debug("Restoring login from stored access token")
316+
self._client.restore_login(
317+
user_id=self._client.user_id,
318+
device_id=self._client.device_id,
319+
access_token=token,
320+
)
321+
response = await self._client.whoami()
322+
if isinstance(response, WhoamiError):
319323
_LOGGER.warning(
320-
"Login by token failed: %s, %s",
324+
"Restoring login from access token failed: %s, %s",
321325
response.status_code,
322326
response.message,
323327
)
328+
self._client.access_token = "" # Force a soft-logout if the homeserver didn't.
329+
elif isinstance(response, WhoamiResponse):
330+
_LOGGER.debug(
331+
"Successfully restored login from access token: user_id '%s', device_id '%s'",
332+
response.user_id,
333+
response.device_id,
334+
)
324335

325336
# If the token login did not succeed
326337
if not self._client.logged_in:

custom_components/matrix/manifest.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
"iot_class": "cloud_push",
77
"issue_tracker": "https://github.com/PaarthShah/matrix-nio-hacs/issues",
88
"loggers": ["matrix_client"],
9-
"requirements": ["matrix-nio==0.20.2", "Pillow==9.5.0"],
10-
"version": "v1.0.0"
9+
"requirements": ["matrix-nio==0.21.2", "Pillow==10.0.0"],
10+
"version": "v1.0.2"
1111
}

requirements.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ colorlog==6.7.0
22
homeassistant>=2023.7.2
33
pip>=21.0,<23.2
44
ruff==0.0.278
5-
matrix-nio==0.21.0
6-
Pillow==10.0.0
5+
matrix-nio==0.21.2
6+
Pillow==10.0.0

tests/conftest.py

+11-5
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
LoginError,
2727
LoginResponse,
2828
Response,
29-
UploadResponse,
29+
UploadResponse, WhoamiError,
3030
)
3131
from pytest_homeassistant_custom_component.common import async_capture_events
3232

@@ -49,6 +49,7 @@
4949
TEST_JOINABLE_ROOMS = ["!RoomIdString:example.com", "#RoomAliasString:example.com"]
5050
TEST_BAD_ROOM = "!UninvitedRoom:example.com"
5151
TEST_MXID = "@user:example.com"
52+
TEST_DEVICE_ID = "FAKEID"
5253
TEST_PASSWORD = "password"
5354
TEST_TOKEN = "access_token"
5455

@@ -63,8 +64,6 @@ def my_enable_custom_integrations(enable_custom_integrations):
6364
class _MockAsyncClient(AsyncClient):
6465
"""Mock class to simulate MatrixBot._client's I/O methods."""
6566

66-
logged_in: bool = False
67-
6867
async def close(self):
6968
return None
7069

@@ -76,17 +75,24 @@ async def join(self, room_id: RoomID):
7675

7776
async def login(self, *args, **kwargs):
7877
if kwargs.get("password") == TEST_PASSWORD or kwargs.get("token") == TEST_TOKEN:
79-
self.logged_in = True
8078
self.access_token = TEST_TOKEN
8179
return LoginResponse(
8280
access_token=TEST_TOKEN,
8381
device_id="test_device",
8482
user_id=TEST_MXID,
8583
)
8684
else:
87-
self.logged_in = False
85+
self.access_token = ""
8886
return LoginError(message="LoginError", status_code="status_code")
8987

88+
async def whoami(self):
89+
if self.access_token == TEST_TOKEN:
90+
self.user_id = TEST_MXID
91+
self.device_id = TEST_DEVICE_ID
92+
else:
93+
self.access_token = ""
94+
return WhoamiError(message="Invalid access token passed.", status_code="M_UNKNOWN_TOKEN")
95+
9096
async def room_send(self, *args, **kwargs):
9197
if not self.logged_in:
9298
raise LocalProtocolError

tests/test_login.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,24 @@ async def test_login(matrix_bot: MatrixBot, mock_client):
1919
assert matrix_bot._client.logged_in
2020

2121
# Test good login using a good password and bad stored access_token.
22-
matrix_bot._auth_tokens = {TEST_MXID: "WrongToken"}
22+
matrix_bot._access_tokens = {TEST_MXID: "WrongToken"}
2323
await matrix_bot._login()
2424
assert matrix_bot._client.logged_in
2525

2626
# Test good login using a good access_token and bad password.
27-
matrix_bot._auth_tokens = {TEST_MXID: TEST_TOKEN}
27+
matrix_bot._access_tokens = {TEST_MXID: TEST_TOKEN}
2828
await matrix_bot._login()
2929
assert matrix_bot._client.logged_in
3030

3131
# Test bad login using a bad password and bad stored access_token.
3232
matrix_bot._password = "WrongPassword"
33-
matrix_bot._auth_tokens = {TEST_MXID: "WrongToken"}
33+
matrix_bot._access_tokens = {TEST_MXID: "WrongToken"}
3434
with pytest.raises(ConfigEntryAuthFailed):
3535
await matrix_bot._login()
3636
assert not matrix_bot._client.logged_in
3737

3838
# Test bad login using bad password and missing access token.
39-
matrix_bot._auth_tokens = {}
39+
matrix_bot._access_tokens = {}
4040
with pytest.raises(ConfigEntryAuthFailed):
4141
await matrix_bot._login()
4242
assert not matrix_bot._client.logged_in

0 commit comments

Comments
 (0)