Skip to content

Commit b4ebc8b

Browse files
Johennespoljar
authored andcommitted
feat(oauth): add flow for granting login by scanning a QR code
Signed-off-by: Johannes Marbach <[email protected]>
1 parent da1369b commit b4ebc8b

File tree

5 files changed

+970
-63
lines changed

5 files changed

+970
-63
lines changed

crates/matrix-sdk/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ All notable changes to this project will be documented in this file.
88

99
### Features
1010

11+
- Extend `authentication::oauth::OAuth::grant_login_with_qr_code` to support granting
12+
login by scanning a QR code on the existing device.
13+
([#5818](https://github.com/matrix-org/matrix-rust-sdk/pull/5818))
1114
- Add a new `RequestConfig::skip_auth()` option. This is useful to ensure that
1215
certain request won't ever include an authorization header.
1316
([#5822](https://github.com/matrix-org/matrix-rust-sdk/pull/5822))

crates/matrix-sdk/src/authentication/oauth/mod.rs

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,10 @@ mod tests;
216216
#[cfg(feature = "e2e-encryption")]
217217
use self::cross_process::{CrossProcessRefreshLockGuard, CrossProcessRefreshManager};
218218
#[cfg(feature = "e2e-encryption")]
219-
use self::qrcode::{GrantLoginWithGeneratedQrCode, LoginWithGeneratedQrCode, LoginWithQrCode};
219+
use self::qrcode::{
220+
GrantLoginWithGeneratedQrCode, GrantLoginWithScannedQrCode, LoginWithGeneratedQrCode,
221+
LoginWithQrCode,
222+
};
220223
pub use self::{
221224
account_management_url::{AccountManagementActionFull, AccountManagementUrlBuilder},
222225
auth_code_builder::{OAuthAuthCodeUrlBuilder, OAuthAuthorizationData},
@@ -1537,7 +1540,7 @@ pub struct GrantLoginWithQrCodeBuilder<'a> {
15371540
#[cfg(feature = "e2e-encryption")]
15381541
impl<'a> GrantLoginWithQrCodeBuilder<'a> {
15391542
/// Create a new builder with the default device creation timeout.
1540-
pub fn new(client: &'a Client) -> Self {
1543+
fn new(client: &'a Client) -> Self {
15411544
Self { client, device_creation_timeout: Duration::from_secs(10) }
15421545
}
15431546

@@ -1552,6 +1555,86 @@ impl<'a> GrantLoginWithQrCodeBuilder<'a> {
15521555
self
15531556
}
15541557

1558+
/// This method allows you to grant login to a new device by scanning a
1559+
/// QR code generated by the new device.
1560+
///
1561+
/// The new device needs to display the QR code which this device can
1562+
/// scan and call this method to grant the login.
1563+
///
1564+
/// A successful login grant using this method will automatically mark the
1565+
/// new device as verified and transfer all end-to-end encryption
1566+
/// related secrets, like the private cross-signing keys and the backup
1567+
/// key from this device device to the new device.
1568+
///
1569+
/// For the reverse flow where this device generates the QR code
1570+
/// for the new device to scan, use
1571+
/// [`GrantLoginWithQrCodeBuilder::generate`].
1572+
///
1573+
/// # Arguments
1574+
///
1575+
/// * `data` - The data scanned from a QR code.
1576+
///
1577+
/// # Example
1578+
///
1579+
/// ```no_run
1580+
/// use anyhow::bail;
1581+
/// use futures_util::StreamExt;
1582+
/// use matrix_sdk::{
1583+
/// Client, authentication::oauth::{
1584+
/// qrcode::{GrantLoginProgress, QrCodeData, QrCodeModeData, QrProgress},
1585+
/// }
1586+
/// };
1587+
/// use std::{error::Error, io::stdin};
1588+
/// # _ = async {
1589+
/// # let bytes = unimplemented!();
1590+
/// // You'll need to use a different library to scan and extract the raw bytes from the QR
1591+
/// // code.
1592+
/// let qr_code_data = QrCodeData::from_bytes(bytes)?;
1593+
///
1594+
/// // Build the client as usual.
1595+
/// let client = Client::builder()
1596+
/// .server_name_or_homeserver_url("matrix.org")
1597+
/// .handle_refresh_tokens()
1598+
/// .build()
1599+
/// .await?;
1600+
///
1601+
/// let oauth = client.oauth();
1602+
///
1603+
/// // Subscribing to the progress is necessary to capture
1604+
/// // the checkcode in order to display it to the other device and to obtain the verification URL to
1605+
/// // open it in a browser so the user can consent to the new login.
1606+
/// let mut grant = oauth.grant_login_with_qr_code().scan(&qr_code_data);
1607+
/// let mut progress = grant.subscribe_to_progress();
1608+
///
1609+
/// // Create a task which will show us the progress and allows us to receive
1610+
/// // and feed back data.
1611+
/// let task = tokio::spawn(async move {
1612+
/// while let Some(state) = progress.next().await {
1613+
/// match state {
1614+
/// GrantLoginProgress::Starting | GrantLoginProgress::SyncingSecrets => (),
1615+
/// GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1616+
/// println!("Please enter the checkcode on your other device: {:?}", check_code);
1617+
/// }
1618+
/// GrantLoginProgress::WaitingForAuth { verification_uri } => {
1619+
/// println!("Please open {verification_uri} to confirm the new login")
1620+
/// },
1621+
/// GrantLoginProgress::Done => break,
1622+
/// }
1623+
/// }
1624+
/// Ok::<(), Box<dyn Error + Send + Sync>>(())
1625+
/// });
1626+
///
1627+
/// // Now run the future to grant the login.
1628+
/// grant.await?;
1629+
/// task.abort();
1630+
///
1631+
/// println!("Successfully granted login");
1632+
/// # anyhow::Ok(()) };
1633+
/// ```
1634+
pub fn scan(self, data: &'a QrCodeData) -> GrantLoginWithScannedQrCode<'a> {
1635+
GrantLoginWithScannedQrCode::new(self.client, data, self.device_creation_timeout)
1636+
}
1637+
15551638
/// This method allows you to grant login to a new device by generating a QR
15561639
/// code on this device to be scanned by the new device.
15571640
///

0 commit comments

Comments
 (0)