Skip to content

Commit 08e1d38

Browse files
Daniel Salinasandybalaam
authored andcommitted
Reorganize QRCode related functionality into its own file
We have a working implementation of the additional forms of QR Code login of MSC4108. This commit moves the existing code into its own file to make future updates easier to follow.
1 parent c3179ea commit 08e1d38

File tree

3 files changed

+175
-166
lines changed

3 files changed

+175
-166
lines changed

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

Lines changed: 8 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@ use std::{fs, num::NonZeroUsize, path::Path, sync::Arc, time::Duration};
22

33
use futures_util::StreamExt;
44
use matrix_sdk::{
5-
authentication::oauth::qrcode::{self, DeviceCodeErrorResponseType, LoginFailureReason},
6-
crypto::{
7-
types::qr_login::{LoginQrCodeDecodeError, QrCodeModeData},
8-
CollectStrategy, TrustRequirement,
9-
},
5+
crypto::{types::qr_login::QrCodeModeData, CollectStrategy, TrustRequirement},
106
encryption::{BackupDownloadStrategy, EncryptionSettings},
117
event_cache::EventCacheError,
128
reqwest::Certificate,
@@ -18,15 +14,19 @@ use matrix_sdk::{
1814
Client as MatrixClient, ClientBuildError as MatrixClientBuildError, HttpError, IdParseError,
1915
RumaApiError, SqliteStoreConfig,
2016
};
21-
use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm};
2217
use ruma::api::error::{DeserializationError, FromHttpResponseError};
2318
use tracing::{debug, error};
2419
use zeroize::Zeroizing;
2520

2621
use super::client::Client;
2722
use crate::{
28-
authentication::OidcConfiguration, client::ClientSessionDelegate, error::ClientError,
29-
helpers::unwrap_or_clone_arc, runtime::get_runtime_handle, task_handle::TaskHandle,
23+
authentication::OidcConfiguration,
24+
client::ClientSessionDelegate,
25+
error::ClientError,
26+
helpers::unwrap_or_clone_arc,
27+
qr_code::{HumanQrLoginError, QrCodeData, QrLoginProgressListener},
28+
runtime::get_runtime_handle,
29+
task_handle::TaskHandle,
3030
};
3131

3232
/// A list of bytes containing a certificate in DER or PEM form.
@@ -39,164 +39,6 @@ enum HomeserverConfig {
3939
ServerNameOrUrl(String),
4040
}
4141

42-
/// Data for the QR code login mechanism.
43-
///
44-
/// The [`QrCodeData`] can be serialized and encoded as a QR code or it can be
45-
/// decoded from a QR code.
46-
#[derive(Debug, uniffi::Object)]
47-
pub struct QrCodeData {
48-
inner: qrcode::QrCodeData,
49-
}
50-
51-
#[matrix_sdk_ffi_macros::export]
52-
impl QrCodeData {
53-
/// Attempt to decode a slice of bytes into a [`QrCodeData`] object.
54-
///
55-
/// The slice of bytes would generally be returned by a QR code decoder.
56-
#[uniffi::constructor]
57-
pub fn from_bytes(bytes: Vec<u8>) -> Result<Arc<Self>, QrCodeDecodeError> {
58-
Ok(Self { inner: qrcode::QrCodeData::from_bytes(&bytes)? }.into())
59-
}
60-
61-
/// The server name contained within the scanned QR code data.
62-
///
63-
/// Note: This value is only present when scanning a QR code the belongs to
64-
/// a logged in client. The mode where the new client shows the QR code
65-
/// will return `None`.
66-
pub fn server_name(&self) -> Option<String> {
67-
match &self.inner.mode_data {
68-
QrCodeModeData::Reciprocate { server_name } => Some(server_name.to_owned()),
69-
QrCodeModeData::Login => None,
70-
}
71-
}
72-
}
73-
74-
/// Error type for the decoding of the [`QrCodeData`].
75-
#[derive(Debug, thiserror::Error, uniffi::Error)]
76-
#[uniffi(flat_error)]
77-
pub enum QrCodeDecodeError {
78-
#[error("Error decoding QR code: {error:?}")]
79-
Crypto {
80-
#[from]
81-
error: LoginQrCodeDecodeError,
82-
},
83-
}
84-
85-
#[derive(Debug, thiserror::Error, uniffi::Error)]
86-
pub enum HumanQrLoginError {
87-
#[error("Linking with this device is not supported.")]
88-
LinkingNotSupported,
89-
#[error("The sign in was cancelled.")]
90-
Cancelled,
91-
#[error("The sign in was not completed in the required time.")]
92-
Expired,
93-
#[error("A secure connection could not have been established between the two devices.")]
94-
ConnectionInsecure,
95-
#[error("The sign in was declined.")]
96-
Declined,
97-
#[error("An unknown error has happened.")]
98-
Unknown,
99-
#[error("The homeserver doesn't provide sliding sync in its configuration.")]
100-
SlidingSyncNotAvailable,
101-
#[error("Unable to use OIDC as the supplied client metadata is invalid.")]
102-
OidcMetadataInvalid,
103-
#[error("The other device is not signed in and as such can't sign in other devices.")]
104-
OtherDeviceNotSignedIn,
105-
}
106-
107-
impl From<qrcode::QRCodeLoginError> for HumanQrLoginError {
108-
fn from(value: qrcode::QRCodeLoginError) -> Self {
109-
use qrcode::{QRCodeLoginError, SecureChannelError};
110-
111-
match value {
112-
QRCodeLoginError::LoginFailure { reason, .. } => match reason {
113-
LoginFailureReason::UnsupportedProtocol => HumanQrLoginError::LinkingNotSupported,
114-
LoginFailureReason::AuthorizationExpired => HumanQrLoginError::Expired,
115-
LoginFailureReason::UserCancelled => HumanQrLoginError::Cancelled,
116-
_ => HumanQrLoginError::Unknown,
117-
},
118-
119-
QRCodeLoginError::OAuth(e) => {
120-
if let Some(e) = e.as_request_token_error() {
121-
match e {
122-
DeviceCodeErrorResponseType::AccessDenied => HumanQrLoginError::Declined,
123-
DeviceCodeErrorResponseType::ExpiredToken => HumanQrLoginError::Expired,
124-
_ => HumanQrLoginError::Unknown,
125-
}
126-
} else {
127-
HumanQrLoginError::Unknown
128-
}
129-
}
130-
131-
QRCodeLoginError::SecureChannel(e) => match e {
132-
SecureChannelError::Utf8(_)
133-
| SecureChannelError::MessageDecode(_)
134-
| SecureChannelError::Json(_)
135-
| SecureChannelError::RendezvousChannel(_) => HumanQrLoginError::Unknown,
136-
SecureChannelError::SecureChannelMessage { .. }
137-
| SecureChannelError::Ecies(_)
138-
| SecureChannelError::InvalidCheckCode => HumanQrLoginError::ConnectionInsecure,
139-
SecureChannelError::InvalidIntent => HumanQrLoginError::OtherDeviceNotSignedIn,
140-
},
141-
142-
QRCodeLoginError::UnexpectedMessage { .. }
143-
| QRCodeLoginError::CrossProcessRefreshLock(_)
144-
| QRCodeLoginError::DeviceKeyUpload(_)
145-
| QRCodeLoginError::SessionTokens(_)
146-
| QRCodeLoginError::UserIdDiscovery(_)
147-
| QRCodeLoginError::SecretImport(_) => HumanQrLoginError::Unknown,
148-
}
149-
}
150-
}
151-
152-
/// Enum describing the progress of the QR-code login.
153-
#[derive(Debug, Default, Clone, uniffi::Enum)]
154-
pub enum QrLoginProgress {
155-
/// The login process is starting.
156-
#[default]
157-
Starting,
158-
/// We established a secure channel with the other device.
159-
EstablishingSecureChannel {
160-
/// The check code that the device should display so the other device
161-
/// can confirm that the channel is secure as well.
162-
check_code: u8,
163-
/// The string representation of the check code, will be guaranteed to
164-
/// be 2 characters long, preserving the leading zero if the
165-
/// first digit is a zero.
166-
check_code_string: String,
167-
},
168-
/// We are waiting for the login and for the OAuth 2.0 authorization server
169-
/// to give us an access token.
170-
WaitingForToken { user_code: String },
171-
/// The login has successfully finished.
172-
Done,
173-
}
174-
175-
#[matrix_sdk_ffi_macros::export(callback_interface)]
176-
pub trait QrLoginProgressListener: SyncOutsideWasm + SendOutsideWasm {
177-
fn on_update(&self, state: QrLoginProgress);
178-
}
179-
180-
impl From<qrcode::LoginProgress> for QrLoginProgress {
181-
fn from(value: qrcode::LoginProgress) -> Self {
182-
use qrcode::LoginProgress;
183-
184-
match value {
185-
LoginProgress::Starting => Self::Starting,
186-
LoginProgress::EstablishingSecureChannel { check_code } => {
187-
let check_code = check_code.to_digit();
188-
189-
Self::EstablishingSecureChannel {
190-
check_code,
191-
check_code_string: format!("{check_code:02}"),
192-
}
193-
}
194-
LoginProgress::WaitingForToken { user_code } => Self::WaitingForToken { user_code },
195-
LoginProgress::Done => Self::Done,
196-
}
197-
}
198-
}
199-
20042
#[derive(Debug, thiserror::Error, uniffi::Error)]
20143
#[uniffi(flat_error)]
20244
pub enum ClientBuildError {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ mod live_location_share;
1818
mod notification;
1919
mod notification_settings;
2020
mod platform;
21+
mod qr_code;
2122
mod room;
2223
mod room_alias;
2324
mod room_directory_search;
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
use std::sync::Arc;
2+
3+
use matrix_sdk::{
4+
authentication::oauth::qrcode::{self, DeviceCodeErrorResponseType, LoginFailureReason},
5+
crypto::types::qr_login::{LoginQrCodeDecodeError, QrCodeModeData},
6+
};
7+
use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm};
8+
use tracing::error;
9+
10+
/// Data for the QR code login mechanism.
11+
///
12+
/// The [`QrCodeData`] can be serialized and encoded as a QR code or it can be
13+
/// decoded from a QR code.
14+
#[derive(Debug, uniffi::Object)]
15+
pub struct QrCodeData {
16+
pub(crate) inner: qrcode::QrCodeData,
17+
}
18+
19+
#[matrix_sdk_ffi_macros::export]
20+
impl QrCodeData {
21+
/// Attempt to decode a slice of bytes into a [`QrCodeData`] object.
22+
///
23+
/// The slice of bytes would generally be returned by a QR code decoder.
24+
#[uniffi::constructor]
25+
pub fn from_bytes(bytes: Vec<u8>) -> Result<Arc<Self>, QrCodeDecodeError> {
26+
Ok(Self { inner: qrcode::QrCodeData::from_bytes(&bytes)? }.into())
27+
}
28+
29+
/// The server name contained within the scanned QR code data.
30+
///
31+
/// Note: This value is only present when scanning a QR code the belongs to
32+
/// a logged in client. The mode where the new client shows the QR code
33+
/// will return `None`.
34+
pub fn server_name(&self) -> Option<String> {
35+
match &self.inner.mode_data {
36+
QrCodeModeData::Reciprocate { server_name } => Some(server_name.to_owned()),
37+
QrCodeModeData::Login => None,
38+
}
39+
}
40+
}
41+
42+
/// Error type for the decoding of the [`QrCodeData`].
43+
#[derive(Debug, thiserror::Error, uniffi::Error)]
44+
#[uniffi(flat_error)]
45+
pub enum QrCodeDecodeError {
46+
#[error("Error decoding QR code: {error:?}")]
47+
Crypto {
48+
#[from]
49+
error: LoginQrCodeDecodeError,
50+
},
51+
}
52+
53+
#[derive(Debug, thiserror::Error, uniffi::Error)]
54+
pub enum HumanQrLoginError {
55+
#[error("Linking with this device is not supported.")]
56+
LinkingNotSupported,
57+
#[error("The sign in was cancelled.")]
58+
Cancelled,
59+
#[error("The sign in was not completed in the required time.")]
60+
Expired,
61+
#[error("A secure connection could not have been established between the two devices.")]
62+
ConnectionInsecure,
63+
#[error("The sign in was declined.")]
64+
Declined,
65+
#[error("An unknown error has happened.")]
66+
Unknown,
67+
#[error("The homeserver doesn't provide sliding sync in its configuration.")]
68+
SlidingSyncNotAvailable,
69+
#[error("Unable to use OIDC as the supplied client metadata is invalid.")]
70+
OidcMetadataInvalid,
71+
#[error("The other device is not signed in and as such can't sign in other devices.")]
72+
OtherDeviceNotSignedIn,
73+
}
74+
75+
impl From<qrcode::QRCodeLoginError> for HumanQrLoginError {
76+
fn from(value: qrcode::QRCodeLoginError) -> Self {
77+
use qrcode::{QRCodeLoginError, SecureChannelError};
78+
79+
match value {
80+
QRCodeLoginError::LoginFailure { reason, .. } => match reason {
81+
LoginFailureReason::UnsupportedProtocol => HumanQrLoginError::LinkingNotSupported,
82+
LoginFailureReason::AuthorizationExpired => HumanQrLoginError::Expired,
83+
LoginFailureReason::UserCancelled => HumanQrLoginError::Cancelled,
84+
_ => HumanQrLoginError::Unknown,
85+
},
86+
87+
QRCodeLoginError::OAuth(e) => {
88+
if let Some(e) = e.as_request_token_error() {
89+
match e {
90+
DeviceCodeErrorResponseType::AccessDenied => HumanQrLoginError::Declined,
91+
DeviceCodeErrorResponseType::ExpiredToken => HumanQrLoginError::Expired,
92+
_ => HumanQrLoginError::Unknown,
93+
}
94+
} else {
95+
HumanQrLoginError::Unknown
96+
}
97+
}
98+
99+
QRCodeLoginError::SecureChannel(e) => match e {
100+
SecureChannelError::Utf8(_)
101+
| SecureChannelError::MessageDecode(_)
102+
| SecureChannelError::Json(_)
103+
| SecureChannelError::RendezvousChannel(_) => HumanQrLoginError::Unknown,
104+
SecureChannelError::SecureChannelMessage { .. }
105+
| SecureChannelError::Ecies(_)
106+
| SecureChannelError::InvalidCheckCode => HumanQrLoginError::ConnectionInsecure,
107+
SecureChannelError::InvalidIntent => HumanQrLoginError::OtherDeviceNotSignedIn,
108+
},
109+
110+
QRCodeLoginError::UnexpectedMessage { .. }
111+
| QRCodeLoginError::CrossProcessRefreshLock(_)
112+
| QRCodeLoginError::DeviceKeyUpload(_)
113+
| QRCodeLoginError::SessionTokens(_)
114+
| QRCodeLoginError::UserIdDiscovery(_)
115+
| QRCodeLoginError::SecretImport(_) => HumanQrLoginError::Unknown,
116+
}
117+
}
118+
}
119+
120+
/// Enum describing the progress of the QR-code login.
121+
#[derive(Debug, Default, Clone, uniffi::Enum)]
122+
pub enum QrLoginProgress {
123+
/// The login process is starting.
124+
#[default]
125+
Starting,
126+
/// We established a secure channel with the other device.
127+
EstablishingSecureChannel {
128+
/// The check code that the device should display so the other device
129+
/// can confirm that the channel is secure as well.
130+
check_code: u8,
131+
/// The string representation of the check code, will be guaranteed to
132+
/// be 2 characters long, preserving the leading zero if the
133+
/// first digit is a zero.
134+
check_code_string: String,
135+
},
136+
/// We are waiting for the login and for the OAuth 2.0 authorization server
137+
/// to give us an access token.
138+
WaitingForToken { user_code: String },
139+
/// The login has successfully finished.
140+
Done,
141+
}
142+
143+
#[matrix_sdk_ffi_macros::export(callback_interface)]
144+
pub trait QrLoginProgressListener: SyncOutsideWasm + SendOutsideWasm {
145+
fn on_update(&self, state: QrLoginProgress);
146+
}
147+
148+
impl From<qrcode::LoginProgress> for QrLoginProgress {
149+
fn from(value: qrcode::LoginProgress) -> Self {
150+
use qrcode::LoginProgress;
151+
152+
match value {
153+
LoginProgress::Starting => Self::Starting,
154+
LoginProgress::EstablishingSecureChannel { check_code } => {
155+
let check_code = check_code.to_digit();
156+
157+
Self::EstablishingSecureChannel {
158+
check_code,
159+
check_code_string: format!("{check_code:02}"),
160+
}
161+
}
162+
LoginProgress::WaitingForToken { user_code } => Self::WaitingForToken { user_code },
163+
LoginProgress::Done => Self::Done,
164+
}
165+
}
166+
}

0 commit comments

Comments
 (0)