@@ -214,7 +214,7 @@ mod tests;
214214#[ cfg( feature = "e2e-encryption" ) ]
215215use self :: cross_process:: { CrossProcessRefreshLockGuard , CrossProcessRefreshManager } ;
216216#[ cfg( feature = "e2e-encryption" ) ]
217- use self :: qrcode:: { LoginWithGeneratedQrCode , LoginWithQrCode } ;
217+ use self :: qrcode:: { GrantLoginWithGeneratedQrCode , LoginWithGeneratedQrCode , LoginWithQrCode } ;
218218pub use self :: {
219219 account_management_url:: { AccountManagementActionFull , AccountManagementUrlBuilder } ,
220220 auth_code_builder:: { OAuthAuthCodeUrlBuilder , OAuthAuthorizationData } ,
@@ -364,7 +364,7 @@ impl OAuth {
364364 as_variant ! ( data, AuthData :: OAuth )
365365 }
366366
367- /// Log in using a QR code.
367+ /// Log in this device using a QR code.
368368 ///
369369 /// # Arguments
370370 ///
@@ -380,6 +380,12 @@ impl OAuth {
380380 LoginWithQrCodeBuilder { client : & self . client , registration_data }
381381 }
382382
383+ /// Grant login to a new device using a QR code.
384+ #[ cfg( feature = "e2e-encryption" ) ]
385+ pub fn grant_login_with_qr_code < ' a > ( & ' a self ) -> GrantLoginWithQrCodeBuilder < ' a > {
386+ GrantLoginWithQrCodeBuilder { client : & self . client }
387+ }
388+
383389 /// Restore or register the OAuth 2.0 client for the server with the given
384390 /// metadata, with the given optional [`ClientRegistrationData`].
385391 ///
@@ -1450,7 +1456,7 @@ impl<'a> LoginWithQrCodeBuilder<'a> {
14501456 /// ruma::serde::Raw,
14511457 /// Client,
14521458 /// };
1453- /// use std::io::stdin;
1459+ /// use std::{error::Error, io::stdin} ;
14541460 /// # fn client_metadata() -> Raw<ClientMetadata> { unimplemented!() }
14551461 /// # _ = async {
14561462 /// // Build the client as usual.
@@ -1481,16 +1487,17 @@ impl<'a> LoginWithQrCodeBuilder<'a> {
14811487 /// LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrScanned(cctx)) => {
14821488 /// println!("Please enter the code displayed on your other device");
14831489 /// let mut s = String::new();
1484- /// stdin().read_line(&mut s).unwrap() ;
1485- /// let check_code = s.trim().parse::<u8>().unwrap() ;
1486- /// cctx.send(check_code).await.unwrap()
1490+ /// stdin().read_line(&mut s)? ;
1491+ /// let check_code = s.trim().parse::<u8>()? ;
1492+ /// cctx.send(check_code).await?
14871493 /// }
14881494 /// LoginProgress::WaitingForToken { user_code } => {
14891495 /// println!("Please use your other device to confirm the log in {user_code}")
14901496 /// },
14911497 /// LoginProgress::Done => break,
14921498 /// }
14931499 /// }
1500+ /// Ok::<(), Box<dyn Error + Send + Sync>>(())
14941501 /// });
14951502 ///
14961503 /// // Now run the future to complete the login.
@@ -1505,6 +1512,82 @@ impl<'a> LoginWithQrCodeBuilder<'a> {
15051512 }
15061513}
15071514
1515+ /// Builder for QR login grant handlers.
1516+ #[ cfg( feature = "e2e-encryption" ) ]
1517+ #[ derive( Debug ) ]
1518+ pub struct GrantLoginWithQrCodeBuilder < ' a > {
1519+ /// The underlying Matrix API client.
1520+ client : & ' a Client ,
1521+ }
1522+
1523+ #[ cfg( feature = "e2e-encryption" ) ]
1524+ impl < ' a > GrantLoginWithQrCodeBuilder < ' a > {
1525+ /// Grant login by generating a QR code on this device to be scanned by the
1526+ /// new device.
1527+ ///
1528+ /// # Example
1529+ ///
1530+ /// ```no_run
1531+ /// use anyhow::bail;
1532+ /// use futures_util::StreamExt;
1533+ /// use matrix_sdk::{
1534+ /// Client, authentication::oauth::{
1535+ /// qrcode::{GeneratedQrProgress, GrantLoginProgress}
1536+ /// }
1537+ /// };
1538+ /// use std::{error::Error, io::stdin};
1539+ /// # _ = async {
1540+ /// // Build the client as usual.
1541+ /// let client = Client::builder()
1542+ /// .server_name_or_homeserver_url("matrix.org")
1543+ /// .handle_refresh_tokens()
1544+ /// .build()
1545+ /// .await?;
1546+ ///
1547+ /// let oauth = client.oauth();
1548+ ///
1549+ /// // Subscribing to the progress is necessary since we need to capture the
1550+ /// // QR code, feed the checkcode back in and obtain the verification URL to
1551+ /// // open it in a browser so the user can consent to the new login.
1552+ /// let mut grant = oauth.grant_login_with_qr_code().generate();
1553+ /// let mut progress = grant.subscribe_to_progress();
1554+ ///
1555+ /// // Create a task which will show us the progress and allows us to receive
1556+ /// // and feed back data.
1557+ /// let task = tokio::spawn(async move {
1558+ /// while let Some(state) = progress.next().await {
1559+ /// match state {
1560+ /// GrantLoginProgress::Starting | GrantLoginProgress::SyncingSecrets => (),
1561+ /// GrantLoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrReady(qr_code_data)) => {
1562+ /// println!("Please scan the QR code on your other device: {:?}", qr_code_data);
1563+ /// }
1564+ /// GrantLoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrScanned(checkcode_sender)) => {
1565+ /// println!("Please enter the code displayed on your other device");
1566+ /// let mut s = String::new();
1567+ /// stdin().read_line(&mut s)?;
1568+ /// let check_code = s.trim().parse::<u8>()?;
1569+ /// checkcode_sender.send(check_code).await?;
1570+ /// }
1571+ /// GrantLoginProgress::WaitingForAuth { verification_uri } => {
1572+ /// println!("Please open {verification_uri} to confirm the new login")
1573+ /// },
1574+ /// GrantLoginProgress::Done => break,
1575+ /// }
1576+ /// }
1577+ /// Ok::<(), Box<dyn Error + Send + Sync>>(())
1578+ /// });
1579+ ///
1580+ /// // Now run the future to grant the login.
1581+ /// grant.await?;
1582+ /// task.abort();
1583+ ///
1584+ /// println!("Successfully granted login");
1585+ /// # anyhow::Ok(()) };
1586+ /// ```
1587+ pub fn generate ( self ) -> GrantLoginWithGeneratedQrCode < ' a > {
1588+ GrantLoginWithGeneratedQrCode :: new ( self . client )
1589+ }
1590+ }
15081591/// A full session for the OAuth 2.0 API.
15091592#[ derive( Debug , Clone ) ]
15101593pub struct OAuthSession {
0 commit comments