diff --git a/TODO.md b/TODO.md index d2d68e07..5b190cc9 100644 --- a/TODO.md +++ b/TODO.md @@ -2,7 +2,6 @@ - Fix right margin of own `\n` messages -- handle invalid access token - If an account is gone from the user's config, discard UI state last page - filter > enter > room list is always scrolled to top - ctrl+j/k when scrolled up diff --git a/src/backend/backend.py b/src/backend/backend.py index 90b3aa7b..d46e0f03 100644 --- a/src/backend/backend.py +++ b/src/backend/backend.py @@ -18,7 +18,7 @@ from appdirs import AppDirs import nio from . import __app_name__ -from .errors import MatrixError +from .errors import MatrixError, MatrixInvalidAccessToken from .matrix_client import MatrixClient from .media_cache import MediaCache from .models import SyncId @@ -305,6 +305,11 @@ class Backend: client = self.clients.pop(user_id, None) if client: + try: + await client.logout() + except MatrixInvalidAccessToken: + pass + self.models["accounts"].pop(user_id, None) self.models["matching_accounts"].pop(user_id, None) self.models[user_id, "uploads"].clear() @@ -317,8 +322,6 @@ class Backend: self.models[user_id, "rooms"].clear() - await client.logout() - await self.saved_accounts.delete(user_id) diff --git a/src/backend/errors.py b/src/backend/errors.py index dcb6aa6e..0c2bfc5f 100644 --- a/src/backend/errors.py +++ b/src/backend/errors.py @@ -33,6 +33,12 @@ class MatrixError(Exception): return cls(response.transport_response.status, response.status_code) +@dataclass +class MatrixInvalidAccessToken(MatrixError): + http_code: int = 401 + m_code: str = "M_UNKNOWN_TOKEN" + + @dataclass class MatrixUnauthorized(MatrixError): http_code: int = 401 diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py index 4a2da1b2..0ee86147 100644 --- a/src/backend/matrix_client.py +++ b/src/backend/matrix_client.py @@ -36,8 +36,9 @@ from nio.crypto import async_generator_from_data from . import __app_name__, __display_name__, utils from .errors import ( BadMimeType, InvalidUserId, InvalidUserInContext, MatrixBadGateway, - MatrixError, MatrixForbidden, MatrixNotFound, MatrixTooLarge, - MatrixUnauthorized, UneededThumbnail, UserFromOtherServerDisallowed, + MatrixError, MatrixForbidden, MatrixInvalidAccessToken, MatrixNotFound, + MatrixTooLarge, MatrixUnauthorized, UneededThumbnail, + UserFromOtherServerDisallowed, ) from .html_markdown import HTML_PROCESSOR as HTML from .media_cache import Media, Thumbnail @@ -47,7 +48,9 @@ from .models.items import ( from .models.model_store import ModelStore from .nio_callbacks import NioCallbacks from .presence import Presence -from .pyotherside_events import AlertRequested, LoopException +from .pyotherside_events import ( + AlertRequested, InvalidAccessToken, LoopException, +) if TYPE_CHECKING: from .backend import Backend @@ -201,6 +204,8 @@ class MatrixClient(nio.AsyncClient): # {room_id: event} self.power_level_events: Dict[str, nio.PowerLevelsEvent] = {} + self.invalid_disconnecting: bool = False + self.nio_callbacks = NioCallbacks(self) @@ -236,7 +241,15 @@ class MatrixClient(nio.AsyncClient): response = await super()._send(*args, **kwargs) if isinstance(response, nio.ErrorResponse): - raise MatrixError.from_nio(response) + try: + raise MatrixError.from_nio(response) + except MatrixInvalidAccessToken: + if not self.invalid_disconnecting: + self.invalid_disconnecting = True + InvalidAccessToken(self.user_id) + await self.backend.logout_client(self.user_id) + + raise return response diff --git a/src/backend/pyotherside_events.py b/src/backend/pyotherside_events.py index d586cd6b..dac16e55 100644 --- a/src/backend/pyotherside_events.py +++ b/src/backend/pyotherside_events.py @@ -98,3 +98,10 @@ class DevicesUpdated(PyOtherSideEvent): """Indicate changes in devices for us or users we share a room with.""" our_user_id: str = field() + + +@dataclass +class InvalidAccessToken(PyOtherSideEvent): + """Indicate one of our account's access token is invalid or revoked.""" + + user_id: str = field() diff --git a/src/gui/Popups/InvalidAccessTokenPopup.qml b/src/gui/Popups/InvalidAccessTokenPopup.qml new file mode 100644 index 00000000..8ee60d45 --- /dev/null +++ b/src/gui/Popups/InvalidAccessTokenPopup.qml @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +import QtQuick 2.12 +import "../Base" +import "../Base/Buttons" + +HFlickableColumnPopup { + id: popup + + property string userId + + signal signBackInRequest() + + page.footer: AutoDirectionLayout { + ApplyButton { + id: signBackButton + text: qsTr("Sign back in") + icon.name: "sign-back-in" + onClicked: { + const page = "Pages/AddAccount/AddAccount.qml" + window.mainUI.pageLoader.show(page) + popup.close() + } + } + + CancelButton { + text: qsTr("Close") + onClicked: popup.close() + } + } + + SummaryLabel { + text: qsTr("Signed out from %1").arg(coloredNameHtml("", userId)) + textFormat: SummaryLabel.StyledText + } + + DetailsLabel { + text: qsTr( + "You have been disconnected from another session, " + + "by the server for security reasons, or the access token in " + + "your configuration file is invalid." + ) + } + + onOpened: signBackButton.forceActiveFocus() +} diff --git a/src/gui/PythonBridge/EventHandlers.qml b/src/gui/PythonBridge/EventHandlers.qml index 83e3538a..89c17301 100644 --- a/src/gui/PythonBridge/EventHandlers.qml +++ b/src/gui/PythonBridge/EventHandlers.qml @@ -94,4 +94,8 @@ QtObject { function onDevicesUpdated(forAccount) { deviceUpdateSignal(forAccount) } + + function onInvalidAccessToken(userId) { + window.makePopup("Popups/InvalidAccessTokenPopup.qml", {userId}) + } } diff --git a/src/gui/PythonBridge/Globals.qml b/src/gui/PythonBridge/Globals.qml index b8b7220c..010d07d1 100644 --- a/src/gui/PythonBridge/Globals.qml +++ b/src/gui/PythonBridge/Globals.qml @@ -5,5 +5,7 @@ import QtQuick 2.12 QtObject { readonly property var pendingCoroutines: ({}) - readonly property var hideErrorTypes: new Set(["gaierror", "SSLError"]) + readonly property var hideErrorTypes: new Set([ + "gaierror", "SSLError", "MatrixInvalidAccessToken", + ]) } diff --git a/src/icons/thin/sign-back-in.svg b/src/icons/thin/sign-back-in.svg new file mode 100644 index 00000000..ee16f93e --- /dev/null +++ b/src/icons/thin/sign-back-in.svg @@ -0,0 +1,3 @@ + + +