diff --git a/bindings/matrix-sdk-ffi/src/client.rs b/bindings/matrix-sdk-ffi/src/client.rs index 29f889f34..c59116236 100644 --- a/bindings/matrix-sdk-ffi/src/client.rs +++ b/bindings/matrix-sdk-ffi/src/client.rs @@ -19,7 +19,7 @@ use matrix_sdk::{ }, Client as MatrixClient, Error, LoopCtrl, }; -use tokio::sync::broadcast; +use tokio::sync::broadcast::{self, error::RecvError}; use tracing::{debug, warn}; use super::{ @@ -73,14 +73,31 @@ impl Client { let (sliding_sync_reset_broadcast_tx, _) = broadcast::channel(1); - Client { + let client = Client { client, state: Arc::new(RwLock::new(state)), delegate: Arc::new(RwLock::new(None)), session_verification_controller, sliding_sync_proxy: Arc::new(RwLock::new(None)), sliding_sync_reset_broadcast_tx, - } + }; + + let mut unknown_token_error_receiver = client.subscribe_to_unknown_token_errors(); + let client_clone = client.clone(); + RUNTIME.spawn(async move { + loop { + match unknown_token_error_receiver.recv().await { + Ok(unknown_token) => client_clone.process_unknown_token_error(unknown_token), + Err(receive_error) => { + if let RecvError::Closed = receive_error { + break; + } + } + } + } + }); + + client } /// Login using a username and password. @@ -347,14 +364,6 @@ impl Client { pub(crate) fn process_sync_error(&self, sync_error: Error) -> LoopCtrl { let client_api_error_kind = sync_error.client_api_error_kind(); match client_api_error_kind { - Some(ErrorKind::UnknownToken { soft_logout }) => { - self.state.write().unwrap().is_soft_logout = *soft_logout; - if let Some(delegate) = &*self.delegate.read().unwrap() { - delegate.did_update_restore_token(); - delegate.did_receive_auth_error(*soft_logout); - } - LoopCtrl::Break - } Some(ErrorKind::UnknownPos) => { let _ = self.sliding_sync_reset_broadcast_tx.send(()); LoopCtrl::Continue @@ -365,6 +374,13 @@ impl Client { } } } + + fn process_unknown_token_error(&self, unknown_token: matrix_sdk::UnknownToken) { + if let Some(delegate) = &*self.delegate.read().unwrap() { + delegate.did_update_restore_token(); + delegate.did_receive_auth_error(unknown_token.soft_logout); + } + } } #[uniffi::export] diff --git a/crates/matrix-sdk/src/client/builder.rs b/crates/matrix-sdk/src/client/builder.rs index bf4790688..98d4e411e 100644 --- a/crates/matrix-sdk/src/client/builder.rs +++ b/crates/matrix-sdk/src/client/builder.rs @@ -27,6 +27,7 @@ use ruma::{ OwnedServerName, ServerName, }; use thiserror::Error; +use tokio::sync::broadcast; #[cfg(not(target_arch = "wasm32"))] use tokio::sync::OnceCell; use tracing::{ @@ -419,6 +420,8 @@ impl ClientBuilder { #[cfg(feature = "experimental-sliding-sync")] let sliding_sync_proxy = sliding_sync_proxy.map(RwLock::new); + let (unknown_token_error_sender, _) = broadcast::channel(1); + let inner = Arc::new(ClientInner { homeserver, authentication_issuer, @@ -441,6 +444,7 @@ impl ClientBuilder { sync_beat: event_listener::Event::new(), handle_refresh_tokens: self.handle_refresh_tokens, refresh_token_lock: Mutex::new(Ok(())), + unknown_token_error_sender, }); debug!("Done building the Client"); diff --git a/crates/matrix-sdk/src/client/mod.rs b/crates/matrix-sdk/src/client/mod.rs index f7ad8fc41..de4f1b6e2 100644 --- a/crates/matrix-sdk/src/client/mod.rs +++ b/crates/matrix-sdk/src/client/mod.rs @@ -68,6 +68,7 @@ use ruma::{ ServerName, UInt, UserId, }; use serde::de::DeserializeOwned; +use tokio::sync::broadcast; #[cfg(not(target_arch = "wasm32"))] use tokio::sync::OnceCell; #[cfg(feature = "e2e-encryption")] @@ -125,6 +126,13 @@ pub enum LoopCtrl { Break, } +/// Wrapper struct for ErrorKind::UnknownToken +#[derive(Debug, Clone)] +pub struct UnknownToken { + /// Whether or not the session was soft logged out + pub soft_logout: bool, +} + /// An async/await enabled Matrix client. /// /// All of the state is held in an `Arc` so the `Client` can be cloned freely. @@ -181,6 +189,9 @@ pub(crate) struct ClientInner { /// wait for the sync to get the data to fetch a room object from the state /// store. pub(crate) sync_beat: event_listener::Event, + /// Client API UnknownToken error publisher. Allows the subscriber logout + /// the user when any request fails because of an invalid access token + pub(crate) unknown_token_error_sender: broadcast::Sender, } #[cfg(not(tarpaulin_include))] @@ -1843,7 +1854,8 @@ impl Client { None => self.homeserver().await.to_string(), }; - self.inner + let response = self + .inner .http_client .send( request, @@ -1853,7 +1865,20 @@ impl Client { self.user_id(), self.server_versions().await?, ) - .await + .await; + + if let Err(http_error) = &response { + if let Some(ErrorKind::UnknownToken { soft_logout }) = + http_error.client_api_error_kind() + { + _ = self + .inner + .unknown_token_error_sender + .send(UnknownToken { soft_logout: *soft_logout }); + } + } + + response } async fn request_server_versions(&self) -> HttpResult> { @@ -2441,6 +2466,12 @@ impl Client { let request = logout::v3::Request::new(); self.send(request, None).await } + + /// Subscribes a new receiver to client UnknownToken errors + pub fn subscribe_to_unknown_token_errors(&self) -> broadcast::Receiver { + let broadcast = &self.inner.unknown_token_error_sender; + broadcast.subscribe() + } } // The http mocking library is not supported for wasm32 diff --git a/crates/matrix-sdk/src/lib.rs b/crates/matrix-sdk/src/lib.rs index ab07b473d..fa6776d32 100644 --- a/crates/matrix-sdk/src/lib.rs +++ b/crates/matrix-sdk/src/lib.rs @@ -49,7 +49,7 @@ mod events; pub use account::Account; #[cfg(feature = "sso-login")] pub use client::SsoLoginBuilder; -pub use client::{Client, ClientBuildError, ClientBuilder, LoginBuilder, LoopCtrl}; +pub use client::{Client, ClientBuildError, ClientBuilder, LoginBuilder, LoopCtrl, UnknownToken}; #[cfg(feature = "image-proc")] pub use error::ImageError; pub use error::{Error, HttpError, HttpResult, RefreshTokenError, Result, RumaApiError};