Correctly handle account with invalid access token
Show a popup saying the session was signed out and cleanup the models data, instead of spamming the users with never-ending errors.
This commit is contained in:
parent
3c9895b0b2
commit
7b6478f514
1
TODO.md
1
TODO.md
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
- Fix right margin of own `<image url>\n<image url>` messages
|
- Fix right margin of own `<image url>\n<image url>` messages
|
||||||
|
|
||||||
- handle invalid access token
|
|
||||||
- If an account is gone from the user's config, discard UI state last page
|
- If an account is gone from the user's config, discard UI state last page
|
||||||
- filter > enter > room list is always scrolled to top
|
- filter > enter > room list is always scrolled to top
|
||||||
- ctrl+j/k when scrolled up
|
- ctrl+j/k when scrolled up
|
||||||
|
|
|
@ -18,7 +18,7 @@ from appdirs import AppDirs
|
||||||
import nio
|
import nio
|
||||||
|
|
||||||
from . import __app_name__
|
from . import __app_name__
|
||||||
from .errors import MatrixError
|
from .errors import MatrixError, MatrixInvalidAccessToken
|
||||||
from .matrix_client import MatrixClient
|
from .matrix_client import MatrixClient
|
||||||
from .media_cache import MediaCache
|
from .media_cache import MediaCache
|
||||||
from .models import SyncId
|
from .models import SyncId
|
||||||
|
@ -305,6 +305,11 @@ class Backend:
|
||||||
client = self.clients.pop(user_id, None)
|
client = self.clients.pop(user_id, None)
|
||||||
|
|
||||||
if client:
|
if client:
|
||||||
|
try:
|
||||||
|
await client.logout()
|
||||||
|
except MatrixInvalidAccessToken:
|
||||||
|
pass
|
||||||
|
|
||||||
self.models["accounts"].pop(user_id, None)
|
self.models["accounts"].pop(user_id, None)
|
||||||
self.models["matching_accounts"].pop(user_id, None)
|
self.models["matching_accounts"].pop(user_id, None)
|
||||||
self.models[user_id, "uploads"].clear()
|
self.models[user_id, "uploads"].clear()
|
||||||
|
@ -317,8 +322,6 @@ class Backend:
|
||||||
|
|
||||||
self.models[user_id, "rooms"].clear()
|
self.models[user_id, "rooms"].clear()
|
||||||
|
|
||||||
await client.logout()
|
|
||||||
|
|
||||||
await self.saved_accounts.delete(user_id)
|
await self.saved_accounts.delete(user_id)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,12 @@ class MatrixError(Exception):
|
||||||
return cls(response.transport_response.status, response.status_code)
|
return cls(response.transport_response.status, response.status_code)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MatrixInvalidAccessToken(MatrixError):
|
||||||
|
http_code: int = 401
|
||||||
|
m_code: str = "M_UNKNOWN_TOKEN"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MatrixUnauthorized(MatrixError):
|
class MatrixUnauthorized(MatrixError):
|
||||||
http_code: int = 401
|
http_code: int = 401
|
||||||
|
|
|
@ -36,8 +36,9 @@ from nio.crypto import async_generator_from_data
|
||||||
from . import __app_name__, __display_name__, utils
|
from . import __app_name__, __display_name__, utils
|
||||||
from .errors import (
|
from .errors import (
|
||||||
BadMimeType, InvalidUserId, InvalidUserInContext, MatrixBadGateway,
|
BadMimeType, InvalidUserId, InvalidUserInContext, MatrixBadGateway,
|
||||||
MatrixError, MatrixForbidden, MatrixNotFound, MatrixTooLarge,
|
MatrixError, MatrixForbidden, MatrixInvalidAccessToken, MatrixNotFound,
|
||||||
MatrixUnauthorized, UneededThumbnail, UserFromOtherServerDisallowed,
|
MatrixTooLarge, MatrixUnauthorized, UneededThumbnail,
|
||||||
|
UserFromOtherServerDisallowed,
|
||||||
)
|
)
|
||||||
from .html_markdown import HTML_PROCESSOR as HTML
|
from .html_markdown import HTML_PROCESSOR as HTML
|
||||||
from .media_cache import Media, Thumbnail
|
from .media_cache import Media, Thumbnail
|
||||||
|
@ -47,7 +48,9 @@ from .models.items import (
|
||||||
from .models.model_store import ModelStore
|
from .models.model_store import ModelStore
|
||||||
from .nio_callbacks import NioCallbacks
|
from .nio_callbacks import NioCallbacks
|
||||||
from .presence import Presence
|
from .presence import Presence
|
||||||
from .pyotherside_events import AlertRequested, LoopException
|
from .pyotherside_events import (
|
||||||
|
AlertRequested, InvalidAccessToken, LoopException,
|
||||||
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .backend import Backend
|
from .backend import Backend
|
||||||
|
@ -201,6 +204,8 @@ class MatrixClient(nio.AsyncClient):
|
||||||
# {room_id: event}
|
# {room_id: event}
|
||||||
self.power_level_events: Dict[str, nio.PowerLevelsEvent] = {}
|
self.power_level_events: Dict[str, nio.PowerLevelsEvent] = {}
|
||||||
|
|
||||||
|
self.invalid_disconnecting: bool = False
|
||||||
|
|
||||||
self.nio_callbacks = NioCallbacks(self)
|
self.nio_callbacks = NioCallbacks(self)
|
||||||
|
|
||||||
|
|
||||||
|
@ -236,7 +241,15 @@ class MatrixClient(nio.AsyncClient):
|
||||||
response = await super()._send(*args, **kwargs)
|
response = await super()._send(*args, **kwargs)
|
||||||
|
|
||||||
if isinstance(response, nio.ErrorResponse):
|
if isinstance(response, nio.ErrorResponse):
|
||||||
|
try:
|
||||||
raise MatrixError.from_nio(response)
|
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
|
return response
|
||||||
|
|
||||||
|
|
|
@ -98,3 +98,10 @@ class DevicesUpdated(PyOtherSideEvent):
|
||||||
"""Indicate changes in devices for us or users we share a room with."""
|
"""Indicate changes in devices for us or users we share a room with."""
|
||||||
|
|
||||||
our_user_id: str = field()
|
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()
|
||||||
|
|
46
src/gui/Popups/InvalidAccessTokenPopup.qml
Normal file
46
src/gui/Popups/InvalidAccessTokenPopup.qml
Normal file
|
@ -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()
|
||||||
|
}
|
|
@ -94,4 +94,8 @@ QtObject {
|
||||||
function onDevicesUpdated(forAccount) {
|
function onDevicesUpdated(forAccount) {
|
||||||
deviceUpdateSignal(forAccount)
|
deviceUpdateSignal(forAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onInvalidAccessToken(userId) {
|
||||||
|
window.makePopup("Popups/InvalidAccessTokenPopup.qml", {userId})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,7 @@ import QtQuick 2.12
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
readonly property var pendingCoroutines: ({})
|
readonly property var pendingCoroutines: ({})
|
||||||
readonly property var hideErrorTypes: new Set(["gaierror", "SSLError"])
|
readonly property var hideErrorTypes: new Set([
|
||||||
|
"gaierror", "SSLError", "MatrixInvalidAccessToken",
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
3
src/icons/thin/sign-back-in.svg
Normal file
3
src/icons/thin/sign-back-in.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m16 2c3.309 0 6 2.691 6 6s-2.691 6-6 6-6-2.691-6-6 2.691-6 6-6zm0-2c-4.418 0-8 3.582-8 8s3.582 8 8 8 8-3.582 8-8-3.582-8-8-8zm-5.405 16.4-1.472 1.6h-3.123v2h-2v2h-2v-2.179l5.903-5.976c-.404-.559-.754-1.158-1.038-1.795l-6.865 6.95v5h6v-2h2v-2h2l2.451-2.663c-.655-.249-1.276-.562-1.856-.937zm7.405-11.4c.551 0 1 .449 1 1s-.449 1-1 1-1-.449-1-1 .449-1 1-1zm0-1c-1.104 0-2 .896-2 2s.896 2 2 2 2-.896 2-2-.896-2-2-2z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 519 B |
Loading…
Reference in New Issue
Block a user