Skip to content

Commit a47d866

Browse files
authored
feat: log out facilities
Merge pull request #1013 from matrix-org/ismail/logout Add logout facilities to `client` and helpers to ffi for tracking the soft-logout issued by a server. Further more this adapts the `sync_with_callback` by adding a new `sync_with_result_callback` that hands the entire `Result` to the callback.
2 parents c500608 + 0643292 commit a47d866

File tree

5 files changed

+203
-45
lines changed

5 files changed

+203
-45
lines changed

bindings/matrix-sdk-ffi/src/api.udl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ interface ClientError {
77

88
callback interface ClientDelegate {
99
void did_receive_sync_update();
10+
void did_receive_auth_error(boolean is_soft_logout);
11+
void did_update_restore_token();
1012
};
1113

1214
interface ClientBuilder {
@@ -29,7 +31,7 @@ interface Client {
2931
void set_delegate(ClientDelegate? delegate);
3032

3133
[Throws=ClientError]
32-
void login(string username, string password);
34+
void login(string username, string password, string? initial_device_name, string? device_id);
3335

3436
[Throws=ClientError]
3537
void restore_login(string restore_token);
@@ -62,6 +64,9 @@ interface Client {
6264

6365
[Throws=ClientError]
6466
SessionVerificationController get_session_verification_controller();
67+
68+
[Throws=ClientError]
69+
void logout();
6570
};
6671

6772
callback interface RoomDelegate {
@@ -182,7 +187,7 @@ interface AuthenticationService {
182187
void configure_homeserver(string server_name);
183188

184189
[Throws=AuthenticationError]
185-
Client login(string username, string password);
190+
Client login(string username, string password, string? initial_device_name, string? device_id);
186191

187192
[Throws=AuthenticationError]
188193
Client restore_with_access_token(string token, string device_id);

bindings/matrix-sdk-ffi/src/authentication_service.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ impl AuthenticationService {
9696
&self,
9797
username: String,
9898
password: String,
99+
initial_device_name: Option<String>,
100+
device_id: Option<String>,
99101
) -> Result<Arc<Client>, AuthenticationError> {
100102
let client = match &*self.client.read().unwrap() {
101103
Some(client) => client.clone(),
@@ -104,7 +106,9 @@ impl AuthenticationService {
104106

105107
// Login and ask the server for the full user ID as this could be different from
106108
// the username that was entered.
107-
client.login(username, password).map_err(AuthenticationError::from)?;
109+
client
110+
.login(username, password, initial_device_name, device_id)
111+
.map_err(AuthenticationError::from)?;
108112
let whoami = client.whoami()?;
109113

110114
// Create a new client to setup the store path now the user ID is known.

bindings/matrix-sdk-ffi/src/client.rs

Lines changed: 95 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,21 @@ use matrix_sdk::{
55
config::SyncSettings,
66
media::{MediaFormat, MediaRequest},
77
ruma::{
8-
api::client::{
9-
account::whoami,
10-
filter::{FilterDefinition, LazyLoadOptions, RoomEventFilter, RoomFilter},
11-
session::get_login_types,
12-
sync::sync_events::v3::Filter,
8+
api::{
9+
client::{
10+
account::whoami,
11+
error::ErrorKind,
12+
filter::{FilterDefinition, LazyLoadOptions, RoomEventFilter, RoomFilter},
13+
session::get_login_types,
14+
sync::sync_events::v3::Filter,
15+
},
16+
error::{FromHttpResponseError, ServerError},
1317
},
1418
events::room::MediaSource,
1519
serde::Raw,
1620
TransactionId,
1721
},
18-
Client as MatrixClient, LoopCtrl, Session,
22+
Client as MatrixClient, Error, HttpError, LoopCtrl, RumaApiError, Session,
1923
};
2024

2125
use super::{
@@ -32,6 +36,8 @@ impl std::ops::Deref for Client {
3236

3337
pub trait ClientDelegate: Sync + Send {
3438
fn did_receive_sync_update(&self);
39+
fn did_receive_auth_error(&self, is_soft_logout: bool);
40+
fn did_update_restore_token(&self);
3541
}
3642

3743
#[derive(Clone)]
@@ -54,18 +60,34 @@ impl Client {
5460
}
5561

5662
/// Login using a username and password.
57-
pub fn login(&self, username: String, password: String) -> anyhow::Result<()> {
63+
pub fn login(
64+
&self,
65+
username: String,
66+
password: String,
67+
initial_device_name: Option<String>,
68+
device_id: Option<String>,
69+
) -> anyhow::Result<()> {
5870
RUNTIME.block_on(async move {
59-
self.client.login_username(&username, &password).send().await?;
71+
let mut builder = self.client.login_username(&username, &password);
72+
if let Some(initial_device_name) = initial_device_name.as_ref() {
73+
builder = builder.initial_device_display_name(initial_device_name);
74+
}
75+
if let Some(device_id) = device_id.as_ref() {
76+
builder = builder.device_id(device_id);
77+
}
78+
builder.send().await?;
6079
Ok(())
6180
})
6281
}
6382

6483
/// Restores the client from a `RestoreToken`.
6584
pub fn restore_login(&self, restore_token: String) -> anyhow::Result<()> {
66-
let RestoreToken { session, homeurl: _, is_guest: _ } =
85+
let RestoreToken { session, homeurl: _, is_guest: _, is_soft_logout } =
6786
serde_json::from_str(&restore_token)?;
6887

88+
// update soft logout state
89+
self.state.write().unwrap().is_soft_logout = is_soft_logout;
90+
6991
self.restore_session(session)
7092
}
7193

@@ -112,6 +134,7 @@ impl Client {
112134
let state = self.state.clone();
113135
let delegate = self.delegate.clone();
114136
let session_verification_controller = self.session_verification_controller.clone();
137+
let local_self = self.clone();
115138
RUNTIME.spawn(async move {
116139
let mut filter = FilterDefinition::default();
117140
let mut room_filter = RoomFilter::default();
@@ -131,31 +154,35 @@ impl Client {
131154
let sync_settings = SyncSettings::new().filter(Filter::FilterId(&filter_id));
132155

133156
client
134-
.sync_with_callback(sync_settings, |sync_response| async {
135-
if !state.read().unwrap().has_first_synced {
136-
state.write().unwrap().has_first_synced = true;
137-
}
138-
139-
if state.read().unwrap().should_stop_syncing {
140-
state.write().unwrap().is_syncing = false;
141-
return LoopCtrl::Break;
142-
} else if !state.read().unwrap().is_syncing {
143-
state.write().unwrap().is_syncing = true;
157+
.sync_with_result_callback(sync_settings, |result| async {
158+
if let Ok(sync_response) = result {
159+
if !state.read().unwrap().has_first_synced {
160+
state.write().unwrap().has_first_synced = true;
161+
}
162+
163+
if state.read().unwrap().should_stop_syncing {
164+
state.write().unwrap().is_syncing = false;
165+
return LoopCtrl::Break;
166+
} else if !state.read().unwrap().is_syncing {
167+
state.write().unwrap().is_syncing = true;
168+
}
169+
170+
if let Some(delegate) = &*delegate.read().unwrap() {
171+
delegate.did_receive_sync_update()
172+
}
173+
174+
if let Some(session_verification_controller) =
175+
&*session_verification_controller.read().await
176+
{
177+
session_verification_controller
178+
.process_to_device_messages(sync_response.to_device)
179+
.await;
180+
}
181+
182+
LoopCtrl::Continue
183+
} else {
184+
local_self.process_sync_error(result.err().unwrap())
144185
}
145-
146-
if let Some(delegate) = &*delegate.read().unwrap() {
147-
delegate.did_receive_sync_update()
148-
}
149-
150-
if let Some(session_verification_controller) =
151-
&*session_verification_controller.read().await
152-
{
153-
session_verification_controller
154-
.process_to_device_messages(sync_response.to_device)
155-
.await;
156-
}
157-
158-
LoopCtrl::Continue
159186
})
160187
.await;
161188
});
@@ -169,6 +196,7 @@ impl Client {
169196
session,
170197
homeurl,
171198
is_guest: self.state.read().unwrap().is_guest,
199+
is_soft_logout: self.state.read().unwrap().is_soft_logout,
172200
})?)
173201
})
174202
}
@@ -258,6 +286,35 @@ impl Client {
258286
Ok(Arc::new(session_verification_controller))
259287
})
260288
}
289+
290+
/// Log out the current user
291+
pub fn logout(&self) -> anyhow::Result<()> {
292+
RUNTIME.block_on(async move {
293+
match self.client.logout().await {
294+
Ok(_) => Ok(()),
295+
Err(error) => Err(anyhow!(error.to_string())),
296+
}
297+
})
298+
}
299+
300+
/// Process a sync error and return loop control accordingly
301+
fn process_sync_error(&self, sync_error: Error) -> LoopCtrl {
302+
let mut control = LoopCtrl::Continue;
303+
if let Error::Http(HttpError::Api(FromHttpResponseError::Server(ServerError::Known(
304+
RumaApiError::ClientApi(error),
305+
)))) = sync_error
306+
{
307+
if let ErrorKind::UnknownToken { soft_logout } = error.kind {
308+
self.state.write().unwrap().is_soft_logout = soft_logout;
309+
if let Some(delegate) = &*self.delegate.read().unwrap() {
310+
delegate.did_update_restore_token();
311+
delegate.did_receive_auth_error(soft_logout);
312+
}
313+
control = LoopCtrl::Break
314+
}
315+
}
316+
control
317+
}
261318
}
262319

263320
#[uniffi::export]
@@ -283,6 +340,11 @@ impl Client {
283340
self.state.read().unwrap().is_guest
284341
}
285342

343+
/// Flag indicating whether the session is in soft logout mode
344+
pub fn is_soft_logout(&self) -> bool {
345+
self.state.read().unwrap().is_soft_logout
346+
}
347+
286348
pub fn rooms(&self) -> Vec<Arc<Room>> {
287349
self.client.rooms().into_iter().map(|room| Arc::new(Room::new(room))).collect()
288350
}

bindings/matrix-sdk-ffi/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,16 @@ pub struct ClientState {
3636
has_first_synced: bool,
3737
is_syncing: bool,
3838
should_stop_syncing: bool,
39+
is_soft_logout: bool,
3940
}
4041

4142
#[derive(Serialize, Deserialize)]
4243
struct RestoreToken {
4344
is_guest: bool,
4445
homeurl: String,
4546
session: Session,
47+
#[serde(default)]
48+
is_soft_logout: bool,
4649
}
4750

4851
#[derive(thiserror::Error, Debug)]

0 commit comments

Comments
 (0)