Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ tempfile = { version = "3.23.0", default-features = false }
thiserror = { version = "2.0.17", default-features = false }
tokio = { version = "1.48.0", default-features = false, features = ["sync"] }
tokio-stream = { version = "0.1.17", default-features = false }
tokio-util = "0.7.17"
tracing = { version = "0.1.41", default-features = false, features = ["std"] }
tracing-appender = { version = "0.2.3", default-features = false }
tracing-core = { version = "0.1.34", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions bindings/matrix-sdk-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ sentry = { workspace = true, optional = true, features = [
] }
sentry-tracing = { workspace = true, optional = true }
thiserror.workspace = true
tokio-util.workspace = true
tracing.workspace = true
tracing-appender.workspace = true
tracing-core.workspace = true
Expand Down
2 changes: 2 additions & 0 deletions bindings/matrix-sdk-ffi/changelog.d/6677.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add `LoginWithQrCodeHandler::abort` and `GrantLoginWithQrCodeHandler::abort` for
requesting cooperative cancellation of the handler.
36 changes: 29 additions & 7 deletions bindings/matrix-sdk-ffi/src/qr_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use matrix_sdk::authentication::oauth::{
};
use matrix_sdk_base::crypto::types::qr_login::{self, QrCodeIntent};
use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm, stream::StreamExt};
use tokio_util::sync::CancellationToken;

use crate::{
authentication::OAuthConfiguration, runtime::get_runtime_handle, task_handle::TaskHandle,
Expand All @@ -33,11 +34,12 @@ use crate::{
pub struct LoginWithQrCodeHandler {
oauth: OAuth,
oauth_configuration: OAuthConfiguration,
cancel: CancellationToken,
}

impl LoginWithQrCodeHandler {
pub(crate) fn new(oauth: OAuth, oauth_configuration: OAuthConfiguration) -> Self {
Self { oauth, oauth_configuration }
Self { oauth, oauth_configuration, cancel: CancellationToken::new() }
}
}

Expand Down Expand Up @@ -75,8 +77,10 @@ impl LoginWithQrCodeHandler {
.registration_data()
.map_err(|_| HumanQrLoginError::OAuthMetadataInvalid)?;

let login =
self.oauth.login_with_qr_code(Some(&registration_data)).scan(&qr_code_data.inner);
let login = self
.oauth
.login_with_qr_code(Some(&registration_data), self.cancel.clone())
.scan(&qr_code_data.inner);

let mut progress = login.subscribe_to_progress();

Expand Down Expand Up @@ -120,7 +124,8 @@ impl LoginWithQrCodeHandler {
.registration_data()
.map_err(|_| HumanQrLoginError::OAuthMetadataInvalid)?;

let login = self.oauth.login_with_qr_code(Some(&registration_data)).generate();
let login =
self.oauth.login_with_qr_code(Some(&registration_data), self.cancel.clone()).generate();

let mut progress = login.subscribe_to_progress();

Expand All @@ -136,17 +141,24 @@ impl LoginWithQrCodeHandler {

Ok(())
}

/// Request the handler to abort cooperatively. This will make the handler
/// tear down its running task and then return the `Cancelled` error.
pub fn abort(&self) {
self.cancel.cancel();
}
}

/// Handler for granting login in with a QR code.
#[derive(uniffi::Object)]
pub struct GrantLoginWithQrCodeHandler {
oauth: OAuth,
cancel: CancellationToken,
}

impl GrantLoginWithQrCodeHandler {
pub(crate) fn new(oauth: OAuth) -> Self {
Self { oauth }
Self { oauth, cancel: CancellationToken::new() }
}
}

Expand Down Expand Up @@ -176,7 +188,8 @@ impl GrantLoginWithQrCodeHandler {
qr_code_data: &QrCodeData,
progress_listener: Box<dyn GrantQrLoginProgressListener>,
) -> Result<(), HumanQrGrantLoginError> {
let grant = self.oauth.grant_login_with_qr_code().scan(&qr_code_data.inner);
let grant =
self.oauth.grant_login_with_qr_code(self.cancel.clone()).scan(&qr_code_data.inner);

let mut progress = grant.subscribe_to_progress();

Expand Down Expand Up @@ -214,7 +227,7 @@ impl GrantLoginWithQrCodeHandler {
self: Arc<Self>,
progress_listener: Box<dyn GrantGeneratedQrLoginProgressListener>,
) -> Result<(), HumanQrGrantLoginError> {
let grant = self.oauth.grant_login_with_qr_code().generate();
let grant = self.oauth.grant_login_with_qr_code(self.cancel.clone()).generate();

let mut progress = grant.subscribe_to_progress();

Expand All @@ -230,6 +243,12 @@ impl GrantLoginWithQrCodeHandler {

Ok(())
}

/// Request the handler to abort cooperatively. This will make the handler
/// tear down its running task and then return the `Cancelled` error.
pub fn abort(&self) {
self.cancel.cancel();
}
}

/// Data for the QR code login mechanism.
Expand Down Expand Up @@ -386,6 +405,8 @@ impl From<qrcode::QRCodeLoginError> for HumanQrLoginError {
| QRCodeLoginError::ServerReset(_) => HumanQrLoginError::Unknown,

QRCodeLoginError::NotFound => HumanQrLoginError::NotFound,

QRCodeLoginError::Cancelled => HumanQrLoginError::Cancelled,
}
}
}
Expand Down Expand Up @@ -489,6 +510,7 @@ impl From<qrcode::QRCodeGrantLoginError> for HumanQrGrantLoginError {
LoginFailureReason::UserCancelled => Self::Cancelled,
_ => Self::Unknown(reason.to_string()),
},
QRCodeGrantLoginError::Cancelled => HumanQrGrantLoginError::Cancelled,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ sha2.workspace = true
tempfile.workspace = true
thiserror.workspace = true
tokio-stream = { workspace = true, features = ["sync"] }
tokio-util = "0.7.17"
tokio-util.workspace = true
tower = { version = "0.5.2", features = ["util"], optional = true }
tracing = { workspace = true, features = ["attributes"] }
uniffi = { workspace = true, optional = true }
Expand Down
3 changes: 3 additions & 0 deletions crates/matrix-sdk/changelog.d/6677.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[**breaking**] Add cancellation token argument to `OAuth::login_with_qr_code` and
`OAuth::grant_login_with_qr_code` for requesting cooperative cancellation of the
process.
63 changes: 50 additions & 13 deletions crates/matrix-sdk/src/authentication/oauth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ use ruma::{
use serde::{Deserialize, Serialize};
use sha2::Digest as _;
use tokio::sync::Mutex;
#[cfg(feature = "e2e-encryption")]
use tokio_util::sync::CancellationToken;
use tracing::{debug, error, instrument, trace, warn};
use url::Url;

Expand Down Expand Up @@ -378,18 +380,27 @@ impl OAuth {
/// the server. If this is not provided, an error will occur unless
/// [`OAuth::register_client()`] or [`OAuth::restore_registered_client()`]
/// was called previously.
/// * `cancel` - A token for requesting the future to abort cooperatively.
#[cfg(feature = "e2e-encryption")]
pub fn login_with_qr_code<'a>(
&'a self,
registration_data: Option<&'a ClientRegistrationData>,
cancel: CancellationToken,
) -> LoginWithQrCodeBuilder<'a> {
LoginWithQrCodeBuilder { client: &self.client, registration_data }
LoginWithQrCodeBuilder::new(&self.client, registration_data, cancel)
}

/// Grant login to a new device using a QR code.
///
/// # Arguments
///
/// * `cancel` - A token for requesting the future to abort cooperatively.
#[cfg(feature = "e2e-encryption")]
pub fn grant_login_with_qr_code<'a>(&'a self) -> GrantLoginWithQrCodeBuilder<'a> {
GrantLoginWithQrCodeBuilder::new(&self.client)
pub fn grant_login_with_qr_code<'a>(
&'a self,
cancel: CancellationToken,
) -> GrantLoginWithQrCodeBuilder<'a> {
GrantLoginWithQrCodeBuilder::new(&self.client, cancel)
}

/// Restore or register the OAuth 2.0 client for the server with the given
Expand Down Expand Up @@ -1357,10 +1368,22 @@ pub struct LoginWithQrCodeBuilder<'a> {

/// The data to restore or register the client with the server.
registration_data: Option<&'a ClientRegistrationData>,

/// A token for requesting the future to abort cooperatively.
cancel: CancellationToken,
}

#[cfg(feature = "e2e-encryption")]
impl<'a> LoginWithQrCodeBuilder<'a> {
/// Create a new builder.
fn new(
client: &'a Client,
registration_data: Option<&'a ClientRegistrationData>,
cancel: CancellationToken,
) -> Self {
Self { client, registration_data, cancel }
}

/// This method allows you to log in with a scanned QR code.
///
/// The existing device needs to display the QR code which this device can
Expand Down Expand Up @@ -1391,6 +1414,7 @@ impl<'a> LoginWithQrCodeBuilder<'a> {
/// ruma::serde::Raw,
/// Client,
/// };
/// use tokio_util::sync::CancellationToken;
/// # fn client_metadata() -> Raw<ClientMetadata> { unimplemented!() }
/// # _ = async {
/// # let bytes = unimplemented!();
Expand All @@ -1416,7 +1440,8 @@ impl<'a> LoginWithQrCodeBuilder<'a> {
///
/// // Subscribing to the progress is necessary since we need to input the check
/// // code on the existing device.
/// let login = oauth.login_with_qr_code(Some(&registration_data)).scan(&qr_code_data);
/// let cancel = CancellationToken::new();
/// let login = oauth.login_with_qr_code(Some(&registration_data), cancel).scan(&qr_code_data);
/// let mut progress = login.subscribe_to_progress();
///
/// // Create a task which will show us the progress and tell us the check
Expand Down Expand Up @@ -1445,7 +1470,7 @@ impl<'a> LoginWithQrCodeBuilder<'a> {
/// # anyhow::Ok(()) };
/// ```
pub fn scan(self, data: &'a QrCodeData) -> LoginWithQrCode<'a> {
LoginWithQrCode::new(self.client, data, self.registration_data)
LoginWithQrCode::new(self.client, data, self.registration_data, self.cancel)
}

/// This method allows you to log in by generating a QR code.
Expand Down Expand Up @@ -1475,6 +1500,7 @@ impl<'a> LoginWithQrCodeBuilder<'a> {
/// Client,
/// };
/// use std::{error::Error, io::stdin};
/// use tokio_util::sync::CancellationToken;
/// # fn client_metadata() -> Raw<ClientMetadata> { unimplemented!() }
/// # _ = async {
/// // Build the client as usual.
Expand All @@ -1490,7 +1516,8 @@ impl<'a> LoginWithQrCodeBuilder<'a> {
///
/// // Subscribing to the progress is necessary since we need to display the
/// // QR code and prompt for the check code.
/// let login = oauth.login_with_qr_code(Some(&registration_data)).generate();
/// let cancel = CancellationToken::new();
/// let login = oauth.login_with_qr_code(Some(&registration_data), cancel).generate();
/// let mut progress = login.subscribe_to_progress();
///
/// // Create a task which will show us the progress and allows us to display
Expand Down Expand Up @@ -1526,7 +1553,7 @@ impl<'a> LoginWithQrCodeBuilder<'a> {
/// # anyhow::Ok(()) };
/// ```
pub fn generate(self) -> LoginWithGeneratedQrCode<'a> {
LoginWithGeneratedQrCode::new(self.client, self.registration_data)
LoginWithGeneratedQrCode::new(self.client, self.registration_data, self.cancel)
}
}

Expand All @@ -1539,13 +1566,15 @@ pub struct GrantLoginWithQrCodeBuilder<'a> {
/// The duration to wait for the homeserver to create the new device after
/// consenting the login before giving up.
device_creation_timeout: Duration,
/// A token for requesting the future to abort cooperatively.
cancel: CancellationToken,
}

#[cfg(feature = "e2e-encryption")]
impl<'a> GrantLoginWithQrCodeBuilder<'a> {
/// Create a new builder with the default device creation timeout.
fn new(client: &'a Client) -> Self {
Self { client, device_creation_timeout: Duration::from_secs(10) }
fn new(client: &'a Client, cancel: CancellationToken) -> Self {
Self { client, device_creation_timeout: Duration::from_secs(10), cancel }
}

/// Set the device creation timeout.
Expand Down Expand Up @@ -1589,6 +1618,7 @@ impl<'a> GrantLoginWithQrCodeBuilder<'a> {
/// }
/// };
/// use std::{error::Error, io::stdin};
/// use tokio_util::sync::CancellationToken;
/// # _ = async {
/// # let bytes = unimplemented!();
/// // You'll need to use a different library to scan and extract the raw bytes from the QR
Expand All @@ -1607,7 +1637,8 @@ impl<'a> GrantLoginWithQrCodeBuilder<'a> {
/// // Subscribing to the progress is necessary to capture
/// // the checkcode in order to display it to the other device and to obtain the verification URL to
/// // open it in a browser so the user can consent to the new login.
/// let mut grant = oauth.grant_login_with_qr_code().scan(&qr_code_data);
/// let cancel = CancellationToken::new();
/// let mut grant = oauth.grant_login_with_qr_code(cancel).scan(&qr_code_data);
/// let mut progress = grant.subscribe_to_progress();
///
/// // Create a task which will show us the progress and allows us to receive
Expand Down Expand Up @@ -1636,7 +1667,12 @@ impl<'a> GrantLoginWithQrCodeBuilder<'a> {
/// # anyhow::Ok(()) };
/// ```
pub fn scan(self, data: &'a QrCodeData) -> GrantLoginWithScannedQrCode<'a> {
GrantLoginWithScannedQrCode::new(self.client, data, self.device_creation_timeout)
GrantLoginWithScannedQrCode::new(
self.client,
data,
self.device_creation_timeout,
self.cancel,
)
}

/// This method allows you to grant login to a new device by generating a QR
Expand Down Expand Up @@ -1664,6 +1700,7 @@ impl<'a> GrantLoginWithQrCodeBuilder<'a> {
/// }
/// };
/// use std::{error::Error, io::stdin};
/// use tokio_util::sync::CancellationToken;
/// # _ = async {
/// // Build the client as usual.
/// let client = Client::builder()
Expand All @@ -1677,7 +1714,7 @@ impl<'a> GrantLoginWithQrCodeBuilder<'a> {
/// // Subscribing to the progress is necessary since we need to capture the
/// // QR code, feed the checkcode back in and obtain the verification URL to
/// // open it in a browser so the user can consent to the new login.
/// let mut grant = oauth.grant_login_with_qr_code().generate();
/// let mut grant = oauth.grant_login_with_qr_code(CancellationToken::new()).generate();
/// let mut progress = grant.subscribe_to_progress();
///
/// // Create a task which will show us the progress and allows us to receive
Expand Down Expand Up @@ -1713,7 +1750,7 @@ impl<'a> GrantLoginWithQrCodeBuilder<'a> {
/// # anyhow::Ok(()) };
/// ```
pub fn generate(self) -> GrantLoginWithGeneratedQrCode<'a> {
GrantLoginWithGeneratedQrCode::new(self.client, self.device_creation_timeout)
GrantLoginWithGeneratedQrCode::new(self.client, self.device_creation_timeout, self.cancel)
}
}
/// A full session for the OAuth 2.0 API.
Expand Down
Loading
Loading