matrix_sdk/authentication/oauth/qrcode/
grant.rs

1// Copyright 2025 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::time::Duration;
16
17use eyeball::SharedObservable;
18use futures_core::Stream;
19use matrix_sdk_base::{
20    boxed_into_future,
21    crypto::types::{
22        SecretsBundle,
23        qr_login::{QrCodeData, QrCodeIntent},
24    },
25};
26use oauth2::VerificationUriComplete;
27use ruma::time::Instant;
28use url::Url;
29#[cfg(doc)]
30use vodozemac::ecies::CheckCode;
31
32use super::{
33    LoginProtocolType, QrAuthMessage,
34    secure_channel::{EstablishedSecureChannel, SecureChannel},
35};
36use crate::{
37    Client,
38    authentication::oauth::qrcode::{
39        CheckCodeSender, GeneratedQrProgress, LoginFailureReason, QRCodeGrantLoginError,
40        QrProgress, SecureChannelError,
41    },
42};
43
44async fn export_secrets_bundle(client: &Client) -> Result<SecretsBundle, QRCodeGrantLoginError> {
45    let secrets_bundle = client
46        .olm_machine()
47        .await
48        .as_ref()
49        .ok_or_else(|| QRCodeGrantLoginError::MissingSecretsBackup(None))?
50        .store()
51        .export_secrets_bundle()
52        .await?;
53    Ok(secrets_bundle)
54}
55
56async fn finish_login_grant<Q>(
57    client: &Client,
58    channel: &mut EstablishedSecureChannel,
59    device_creation_timeout: Duration,
60    secrets_bundle: &SecretsBundle,
61    state: &SharedObservable<GrantLoginProgress<Q>>,
62) -> Result<(), QRCodeGrantLoginError> {
63    // The new device registers with the authorization server and sends it a device
64    // authorization authorization request.
65    // -- MSC4108 OAuth 2.0 login step 2
66
67    // We wait for the new device to send us the m.login.protocol message with the
68    // device authorization grant information. -- MSC4108 OAuth 2.0 login step 3
69    let message = channel.receive_json().await?;
70    let QrAuthMessage::LoginProtocol { device_authorization_grant, protocol, device_id } = message
71    else {
72        return Err(QRCodeGrantLoginError::Unknown(
73            "Receiving unexpected message when expecting LoginProtocol".to_owned(),
74        ));
75    };
76
77    // We verify the selected protocol.
78    // -- MSC4108 OAuth 2.0 login step 4
79    if protocol != LoginProtocolType::DeviceAuthorizationGrant {
80        channel
81            .send_json(QrAuthMessage::LoginFailure {
82                reason: LoginFailureReason::UnsupportedProtocol,
83                homeserver: None,
84            })
85            .await?;
86        return Err(QRCodeGrantLoginError::UnsupportedProtocol(protocol));
87    }
88
89    // We check that the device ID is still available.
90    // -- MSC4108 OAuth 2.0 login step 4 continued
91    if !matches!(client.device_exists(device_id.clone().into()).await, Ok(false)) {
92        channel
93            .send_json(QrAuthMessage::LoginFailure {
94                reason: LoginFailureReason::DeviceAlreadyExists,
95                homeserver: None,
96            })
97            .await?;
98        return Err(QRCodeGrantLoginError::DeviceIDAlreadyInUse);
99    }
100
101    // We emit an update so that the caller can open the verification URI in a
102    // system browser to consent to the login.
103    // -- MSC4108 OAuth 2.0 login step 4 continued
104    let verification_uri = Url::parse(
105        device_authorization_grant
106            .verification_uri_complete
107            .map(VerificationUriComplete::into_secret)
108            .unwrap_or(device_authorization_grant.verification_uri.to_string())
109            .as_str(),
110    )
111    .map_err(|_| QRCodeGrantLoginError::UnableToCreateDevice)?;
112    state.set(GrantLoginProgress::WaitingForAuth { verification_uri });
113
114    // We send the new device the m.login.protocol_accepted message to let it know
115    // that the consent process is in progress.
116    // -- MSC4108 OAuth 2.0 login step 4 continued
117    let message = QrAuthMessage::LoginProtocolAccepted;
118    channel.send_json(&message).await?;
119
120    // The new device displays the user code it received from the authorization
121    // server and starts polling for an access token. In parallel, the user
122    // consents to the new login in the browser on this device, while verifying
123    // the user code displayed on the other device. -- MSC4108 OAuth 2.0 login
124    // steps 5 & 6
125
126    // We wait for the new device to send us the m.login.success message
127    let message: QrAuthMessage = channel.receive_json().await?;
128    let QrAuthMessage::LoginSuccess = message else {
129        return Err(QRCodeGrantLoginError::Unknown(
130            "Receiving unexpected message when expecting LoginSuccess".to_owned(),
131        ));
132    };
133
134    // We check that the new device was created successfully, allowing for the
135    // specified delay. -- MSC4108 Secret sharing and device verification step 1
136    let deadline = Instant::now() + device_creation_timeout;
137
138    loop {
139        if matches!(client.device_exists(device_id.clone().into()).await, Ok(true)) {
140            break;
141        } else {
142            // If the deadline hasn't yet passed, give it some time and retry the request.
143            if Instant::now() < deadline {
144                tokio::time::sleep(Duration::from_millis(500)).await;
145                continue;
146            } else {
147                // The deadline has passed. Let's fail the login process.
148                channel
149                    .send_json(QrAuthMessage::LoginFailure {
150                        reason: LoginFailureReason::DeviceNotFound,
151                        homeserver: None,
152                    })
153                    .await?;
154                return Err(QRCodeGrantLoginError::DeviceIDAlreadyInUse);
155            }
156        }
157    }
158
159    // We send the new device the secrets bundle.
160    // -- MSC4108 Secret sharing and device verification step 2
161    state.set(GrantLoginProgress::SyncingSecrets);
162    let message = QrAuthMessage::LoginSecrets(secrets_bundle.clone());
163    channel.send_json(&message).await?;
164
165    // And we're done.
166    state.set(GrantLoginProgress::Done);
167
168    Ok(())
169}
170
171/// The progress of granting the login.
172#[derive(Clone, Debug, Default)]
173pub enum GrantLoginProgress<Q> {
174    /// We're just starting up, this is the default and initial state.
175    #[default]
176    Starting,
177    /// The secure channel is being established by exchanging the QR code
178    /// and/or [`CheckCode`].
179    EstablishingSecureChannel(Q),
180    /// The secure channel has been confirmed using the [`CheckCode`] and this
181    /// device is waiting for the authorization to complete.
182    WaitingForAuth {
183        /// A URI to open in a (secure) system browser to verify the new login.
184        verification_uri: Url,
185    },
186    /// The new device has been granted access and this device is sending the
187    /// secrets to it.
188    SyncingSecrets,
189    /// The process is complete.
190    Done,
191}
192
193/// Named future for granting login by scanning a QR code on this, existing,
194/// device that was generated by the other, new, device.
195#[derive(Debug)]
196pub struct GrantLoginWithScannedQrCode<'a> {
197    client: &'a Client,
198    qr_code_data: &'a QrCodeData,
199    device_creation_timeout: Duration,
200    state: SharedObservable<GrantLoginProgress<QrProgress>>,
201}
202
203impl<'a> GrantLoginWithScannedQrCode<'a> {
204    pub(crate) fn new(
205        client: &'a Client,
206        qr_code_data: &'a QrCodeData,
207        device_creation_timeout: Duration,
208    ) -> GrantLoginWithScannedQrCode<'a> {
209        GrantLoginWithScannedQrCode {
210            client,
211            qr_code_data,
212            device_creation_timeout,
213            state: Default::default(),
214        }
215    }
216}
217
218impl GrantLoginWithScannedQrCode<'_> {
219    /// Subscribe to the progress of QR code login.
220    ///
221    /// It's necessary to subscribe to this to capture the [`CheckCode`] in
222    /// order to display it to the other device and to obtain the
223    /// verification URL for consenting to the login.
224    pub fn subscribe_to_progress(
225        &self,
226    ) -> impl Stream<Item = GrantLoginProgress<QrProgress>> + use<> {
227        self.state.subscribe()
228    }
229}
230
231impl<'a> IntoFuture for GrantLoginWithScannedQrCode<'a> {
232    type Output = Result<(), QRCodeGrantLoginError>;
233    boxed_into_future!(extra_bounds: 'a);
234
235    fn into_future(self) -> Self::IntoFuture {
236        Box::pin(async move {
237            // Before we get here, the other device has created a new rendezvous session
238            // and presented a QR code which this device has scanned.
239            // -- MSC4108 Secure channel setup steps 1-3
240
241            // First things first, export the secrets bundle and establish the secure
242            // channel. Since we're the one that scanned the QR code, we're
243            // certain that the secure channel is secure, under the assumption
244            // that we didn't scan the wrong QR code. -- MSC4108 Secure channel
245            // setup steps 3-5
246            let secrets_bundle = export_secrets_bundle(self.client).await?;
247
248            let mut channel = EstablishedSecureChannel::from_qr_code(
249                self.client.inner.http_client.inner.clone(),
250                self.qr_code_data,
251                QrCodeIntent::Reciprocate,
252            )
253            .await?;
254
255            // The other side isn't yet sure that it's talking to the right device, show
256            // a check code so they can confirm.
257            // -- MSC4108 Secure channel setup step 6
258            let check_code = channel.check_code().to_owned();
259            self.state
260                .set(GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }));
261
262            // The user now enters the checkcode on the other device which verifies it
263            // and will only continue requesting the login if the code matches.
264            // -- MSC4108 Secure channel setup step 7
265
266            // Inform the other device about the available login protocols and the
267            // homeserver to use.
268            // -- MSC4108 OAuth 2.0 login step 1
269            let message = QrAuthMessage::LoginProtocols {
270                protocols: vec![LoginProtocolType::DeviceAuthorizationGrant],
271                homeserver: self.client.homeserver(),
272            };
273            channel.send_json(message).await?;
274
275            // Proceed with granting the login.
276            // -- MSC4108 OAuth 2.0 login remaining steps
277            finish_login_grant(
278                self.client,
279                &mut channel,
280                self.device_creation_timeout,
281                &secrets_bundle,
282                &self.state,
283            )
284            .await
285        })
286    }
287}
288
289/// Named future for granting login by generating a QR code on this, existing,
290/// device to be scanned by the other, new, device.
291#[derive(Debug)]
292pub struct GrantLoginWithGeneratedQrCode<'a> {
293    client: &'a Client,
294    device_creation_timeout: Duration,
295    state: SharedObservable<GrantLoginProgress<GeneratedQrProgress>>,
296}
297
298impl<'a> GrantLoginWithGeneratedQrCode<'a> {
299    pub(crate) fn new(
300        client: &'a Client,
301        device_creation_timeout: Duration,
302    ) -> GrantLoginWithGeneratedQrCode<'a> {
303        GrantLoginWithGeneratedQrCode { client, device_creation_timeout, state: Default::default() }
304    }
305}
306
307impl GrantLoginWithGeneratedQrCode<'_> {
308    /// Subscribe to the progress of QR code login.
309    ///
310    /// It's necessary to subscribe to this to capture the QR code in order to
311    /// display it to the other device, to feed the [`CheckCode`] entered by the
312    /// user back in and to obtain the verification URL for consenting to
313    /// the login.
314    pub fn subscribe_to_progress(
315        &self,
316    ) -> impl Stream<Item = GrantLoginProgress<GeneratedQrProgress>> + use<> {
317        self.state.subscribe()
318    }
319}
320
321impl<'a> IntoFuture for GrantLoginWithGeneratedQrCode<'a> {
322    type Output = Result<(), QRCodeGrantLoginError>;
323    boxed_into_future!(extra_bounds: 'a);
324
325    fn into_future(self) -> Self::IntoFuture {
326        Box::pin(async move {
327            // Create a new ephemeral key pair and a rendezvous session to grant a
328            // login with.
329            // -- MSC4108 Secure channel setup steps 1 & 2
330            let homeserver_url = self.client.homeserver();
331            let http_client = self.client.inner.http_client.clone();
332            let secrets_bundle = export_secrets_bundle(self.client).await?;
333            let channel = SecureChannel::reciprocate(http_client, &homeserver_url).await?;
334
335            // Extract the QR code data and emit an update so that the caller can
336            // present the QR code for scanning by the new device.
337            // -- MSC4108 Secure channel setup step 3
338            self.state.set(GrantLoginProgress::EstablishingSecureChannel(
339                GeneratedQrProgress::QrReady(channel.qr_code_data().clone()),
340            ));
341
342            // Wait for the secure channel to connect. The other device now needs to scan
343            // the QR code and send us the LoginInitiateMessage which we respond to
344            // with the LoginOkMessage. -- MSC4108 step 4 & 5
345            let channel = channel.connect().await?;
346
347            // The other device now needs to verify our message, compute the checkcode and
348            // display it. We emit a progress update to let the caller prompt the
349            // user to enter the checkcode and feed it back to us.
350            // -- MSC4108 Secure channel setup step 6
351            let (tx, rx) = tokio::sync::oneshot::channel();
352            self.state.set(GrantLoginProgress::EstablishingSecureChannel(
353                GeneratedQrProgress::QrScanned(CheckCodeSender::new(tx)),
354            ));
355            let check_code = rx.await.map_err(|_| SecureChannelError::CannotReceiveCheckCode)?;
356
357            // Use the checkcode to verify that the channel is actually secure.
358            // -- MSC4108 Secure channel setup step 7
359            let mut channel = channel.confirm(check_code)?;
360
361            // Since the QR code was generated on this existing device, the new device can
362            // derive the homeserver to use for logging in from the QR code and we
363            // don't need to send the m.login.protocols message.
364            // -- MSC4108 OAuth 2.0 login step 1
365
366            // Proceed with granting the login.
367            // -- MSC4108 OAuth 2.0 login remaining steps
368            finish_login_grant(
369                self.client,
370                &mut channel,
371                self.device_creation_timeout,
372                &secrets_bundle,
373                &self.state,
374            )
375            .await
376        })
377    }
378}
379
380#[cfg(all(test, not(target_family = "wasm")))]
381mod test {
382    use assert_matches2::{assert_let, assert_matches};
383    use futures_util::StreamExt;
384    use matrix_sdk_base::crypto::types::SecretsBundle;
385    use matrix_sdk_common::executor::spawn;
386    use matrix_sdk_test::async_test;
387    use oauth2::{EndUserVerificationUrl, VerificationUriComplete};
388    use ruma::{owned_device_id, owned_user_id};
389    use tokio::sync::oneshot;
390    use tracing::debug;
391
392    use super::*;
393    use crate::{
394        authentication::oauth::qrcode::{
395            LoginFailureReason, QrAuthMessage,
396            messages::{AuthorizationGrant, LoginProtocolType},
397            secure_channel::{EstablishedSecureChannel, test::MockedRendezvousServer},
398        },
399        http_client::HttpClient,
400        test_utils::mocks::MatrixMockServer,
401    };
402
403    enum BobBehaviour {
404        HappyPath,
405        UnexpectedMessageInsteadOfLoginProtocol,
406        DeviceAlreadyExists,
407        DeviceNotCreated,
408    }
409
410    #[allow(clippy::too_many_arguments)]
411    async fn request_login_with_scanned_qr_code(
412        behaviour: BobBehaviour,
413        qr_code_rx: oneshot::Receiver<QrCodeData>,
414        check_code_tx: oneshot::Sender<u8>,
415        server: MatrixMockServer,
416        // The rendezvous server is here because it contains MockGuards that are tied to the
417        // lifetime of the MatrixMockServer. Otherwise we might attempt to drop the
418        // MatrixMockServer before the MockGuards.
419        _rendezvous_server: MockedRendezvousServer,
420        device_authorization_grant: Option<AuthorizationGrant>,
421        secrets_bundle: Option<SecretsBundle>,
422    ) {
423        // Wait for Alice to produce the qr code.
424        let qr_code_data = qr_code_rx.await.expect("Bob should receive the QR code");
425
426        // Use the QR code to establish the secure channel from the new client (Bob).
427        let mut bob = EstablishedSecureChannel::from_qr_code(
428            reqwest::Client::new(),
429            &qr_code_data,
430            QrCodeIntent::Login,
431        )
432        .await
433        .expect("Bob should be able to connect the secure channel");
434
435        // Let Alice know about the checkcode so she can verify the channel.
436        check_code_tx
437            .send(bob.check_code().to_digit())
438            .expect("Bob should be able to send the checkcode");
439
440        match behaviour {
441            BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol => {
442                // Send an unexpected message and exit.
443                let message = QrAuthMessage::LoginSuccess;
444                bob.send_json(message).await.unwrap();
445                return;
446            }
447            BobBehaviour::DeviceAlreadyExists => {
448                // Mock the endpoint for querying devices so that Alice thinks the device
449                // already exists.
450                server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
451
452                // Now send the LoginProtocol message.
453                let message = QrAuthMessage::LoginProtocol {
454                    protocol: LoginProtocolType::DeviceAuthorizationGrant,
455                    device_authorization_grant: device_authorization_grant
456                        .expect("Bob needs the device authorization grant"),
457                    device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
458                };
459                bob.send_json(message).await.unwrap();
460
461                // Alice should fail the login with the appropriate reason.
462                let message = bob
463                    .receive_json()
464                    .await
465                    .expect("Bob should receive the LoginFailure message from Alice");
466                assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
467                assert_matches!(reason, LoginFailureReason::DeviceAlreadyExists);
468
469                return; // Exit.
470            }
471            _ => {
472                // Send the LoginProtocol message.
473                let message = QrAuthMessage::LoginProtocol {
474                    protocol: LoginProtocolType::DeviceAuthorizationGrant,
475                    device_authorization_grant: device_authorization_grant
476                        .expect("Bob needs the device authorization grant"),
477                    device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
478                };
479                bob.send_json(message).await.unwrap();
480            }
481        }
482
483        // Receive the LoginProtocolAccepted message.
484        let message = bob
485            .receive_json()
486            .await
487            .expect("Bob should receive the LoginProtocolAccepted message from Alice");
488        assert_let!(QrAuthMessage::LoginProtocolAccepted = message);
489
490        match behaviour {
491            BobBehaviour::DeviceNotCreated => {
492                // Don't mock the endpoint for querying devices so that Alice cannot verify that
493                // we have logged in.
494
495                // Send the LoginSuccess message to claim that we have logged in.
496                let message = QrAuthMessage::LoginSuccess;
497                bob.send_json(message).await.unwrap();
498
499                // Alice should eventually give up querying our device and fail the login with
500                // the appropriate reason.
501                let message = bob
502                    .receive_json()
503                    .await
504                    .expect("Bob should receive the LoginFailure message from Alice");
505                assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
506                assert_matches!(reason, LoginFailureReason::DeviceNotFound);
507
508                return; // Exit.
509            }
510            _ => {
511                // Mock the endpoint for querying devices so that Alice thinks we have logged
512                // in.
513                server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
514
515                // Send the LoginSuccess message.
516                let message = QrAuthMessage::LoginSuccess;
517                bob.send_json(message).await.unwrap();
518            }
519        }
520
521        // Receive the LoginSecrets message.
522        let message = bob
523            .receive_json()
524            .await
525            .expect("Bob should receive the LoginSecrets message from Alice");
526        assert_let!(QrAuthMessage::LoginSecrets(bundle) = message);
527
528        // Verify that we received the correct secrets.
529        assert_eq!(
530            serde_json::to_value(&secrets_bundle).unwrap(),
531            serde_json::to_value(&bundle).unwrap()
532        );
533    }
534
535    #[allow(clippy::too_many_arguments)]
536    async fn request_login_with_generated_qr_code(
537        behaviour: BobBehaviour,
538        channel: SecureChannel,
539        check_code_rx: oneshot::Receiver<u8>,
540        server: MatrixMockServer,
541        // The rendezvous server is here because it contains MockGuards that are tied to the
542        // lifetime of the MatrixMockServer. Otherwise we might attempt to drop the
543        // MatrixMockServer before the MockGuards.
544        _rendezvous_server: MockedRendezvousServer,
545        homeserver: Url,
546        device_authorization_grant: Option<AuthorizationGrant>,
547        secrets_bundle: Option<SecretsBundle>,
548    ) {
549        // Wait for Alice to scan the qr code and connect the secure channel.
550        let channel =
551            channel.connect().await.expect("Bob should be able to connect the secure channel");
552
553        // Wait for Alice to send us the checkcode and use it to verify the channel.
554        let check_code = check_code_rx.await.expect("Bob should receive the checkcode");
555        let mut bob = channel
556            .confirm(check_code)
557            .expect("Bob should be able to confirm the channel is secure");
558
559        // Receive the LoginProtocols message.
560        let message = bob
561            .receive_json()
562            .await
563            .expect("Bob should receive the LoginProtocolAccepted message from Alice");
564        assert_let!(
565            QrAuthMessage::LoginProtocols { protocols, homeserver: alice_homeserver } = message
566        );
567        assert_eq!(protocols, vec![LoginProtocolType::DeviceAuthorizationGrant]);
568        assert_eq!(alice_homeserver, homeserver);
569
570        match behaviour {
571            BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol => {
572                // Send an unexpected message and exit.
573                let message = QrAuthMessage::LoginSuccess;
574                bob.send_json(message).await.unwrap();
575                return;
576            }
577            BobBehaviour::DeviceAlreadyExists => {
578                // Mock the endpoint for querying devices so that Alice thinks the device
579                // already exists.
580                server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
581
582                // Now send the LoginProtocol message.
583                let message = QrAuthMessage::LoginProtocol {
584                    protocol: LoginProtocolType::DeviceAuthorizationGrant,
585                    device_authorization_grant: device_authorization_grant
586                        .expect("Bob needs the device authorization grant"),
587                    device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
588                };
589                bob.send_json(message).await.unwrap();
590
591                // Alice should fail the login with the appropriate reason.
592                let message = bob
593                    .receive_json()
594                    .await
595                    .expect("Bob should receive the LoginFailure message from Alice");
596                assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
597                assert_matches!(reason, LoginFailureReason::DeviceAlreadyExists);
598
599                return; // Exit.
600            }
601            _ => {
602                // Send the LoginProtocol message.
603                let message = QrAuthMessage::LoginProtocol {
604                    protocol: LoginProtocolType::DeviceAuthorizationGrant,
605                    device_authorization_grant: device_authorization_grant
606                        .expect("Bob needs the device authorization grant"),
607                    device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
608                };
609                bob.send_json(message).await.unwrap();
610            }
611        }
612
613        // Receive the LoginProtocolAccepted message.
614        let message = bob
615            .receive_json()
616            .await
617            .expect("Bob should receive the LoginProtocolAccepted message from Alice");
618        assert_let!(QrAuthMessage::LoginProtocolAccepted = message);
619
620        match behaviour {
621            BobBehaviour::DeviceNotCreated => {
622                // Don't mock the endpoint for querying devices so that Alice cannot verify that
623                // we have logged in.
624
625                // Send the LoginSuccess message to claim that we have logged in.
626                let message = QrAuthMessage::LoginSuccess;
627                bob.send_json(message).await.unwrap();
628
629                // Alice should eventually give up querying our device and fail the login with
630                // the appropriate reason.
631                let message = bob
632                    .receive_json()
633                    .await
634                    .expect("Bob should receive the LoginFailure message from Alice");
635                assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
636                assert_matches!(reason, LoginFailureReason::DeviceNotFound);
637
638                return; // Exit.
639            }
640            _ => {
641                // Mock the endpoint for querying devices so that Alice thinks we have logged
642                // in.
643                server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
644
645                // Send the LoginSuccess message.
646                let message = QrAuthMessage::LoginSuccess;
647                bob.send_json(message).await.unwrap();
648            }
649        }
650
651        // Receive the LoginSecrets message.
652        let message = bob
653            .receive_json()
654            .await
655            .expect("Bob should receive the LoginSecrets message from Alice");
656        assert_let!(QrAuthMessage::LoginSecrets(bundle) = message);
657
658        // Verify that we received the correct secrets.
659        assert_eq!(
660            serde_json::to_value(&secrets_bundle).unwrap(),
661            serde_json::to_value(&bundle).unwrap()
662        );
663    }
664
665    #[async_test]
666    async fn test_grant_login_with_generated_qr_code() {
667        let server = MatrixMockServer::new().await;
668        let rendezvous_server =
669            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
670        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
671
672        let device_authorization_grant = AuthorizationGrant {
673            verification_uri_complete: Some(VerificationUriComplete::new(
674                "https://id.matrix.org/device/abcde".to_owned(),
675            )),
676            verification_uri: EndUserVerificationUrl::new(
677                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
678            )
679            .unwrap(),
680        };
681
682        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
683        server
684            .mock_upload_cross_signing_keys()
685            .ok()
686            .expect(1)
687            .named("upload_xsigning_keys")
688            .mount()
689            .await;
690        server
691            .mock_upload_cross_signing_signatures()
692            .ok()
693            .expect(1)
694            .named("upload_xsigning_signatures")
695            .mount()
696            .await;
697
698        // Create the existing client (Alice).
699        let user_id = owned_user_id!("@alice:example.org");
700        let device_id = owned_device_id!("ALICE_DEVICE");
701        let alice = server
702            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
703            .logged_in_with_oauth()
704            .build()
705            .await;
706        alice
707            .encryption()
708            .bootstrap_cross_signing(None)
709            .await
710            .expect("Alice should be able to set up cross signing");
711
712        // Prepare the login granting future.
713        let oauth = alice.oauth();
714        let grant = oauth
715            .grant_login_with_qr_code()
716            .device_creation_timeout(Duration::from_secs(2))
717            .generate();
718        let secrets_bundle = export_secrets_bundle(&alice)
719            .await
720            .expect("Alice should be able to export the secrets bundle");
721        let (qr_code_tx, qr_code_rx) = oneshot::channel();
722        let (checkcode_tx, checkcode_rx) = oneshot::channel();
723
724        // Spawn the updates task.
725        let mut updates = grant.subscribe_to_progress();
726        let mut state = grant.state.get();
727        let verification_uri_complete =
728            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
729        assert_matches!(state.clone(), GrantLoginProgress::Starting);
730        let updates_task = spawn(async move {
731            let mut qr_code_tx = Some(qr_code_tx);
732            let mut checkcode_rx = Some(checkcode_rx);
733
734            while let Some(update) = updates.next().await {
735                match &update {
736                    GrantLoginProgress::Starting => {
737                        assert_matches!(state, GrantLoginProgress::Starting);
738                    }
739                    GrantLoginProgress::EstablishingSecureChannel(
740                        GeneratedQrProgress::QrReady(qr_code_data),
741                    ) => {
742                        assert_matches!(state, GrantLoginProgress::Starting);
743                        qr_code_tx
744                            .take()
745                            .expect("The QR code should only be forwarded once")
746                            .send(qr_code_data.clone())
747                            .expect("Alice should be able to forward the QR code");
748                    }
749                    GrantLoginProgress::EstablishingSecureChannel(
750                        GeneratedQrProgress::QrScanned(checkcode_sender),
751                    ) => {
752                        assert_matches!(
753                            state,
754                            GrantLoginProgress::EstablishingSecureChannel(
755                                GeneratedQrProgress::QrReady(_)
756                            )
757                        );
758                        let checkcode = checkcode_rx
759                            .take()
760                            .expect("The checkcode should only be forwarded once")
761                            .await
762                            .expect("Alice should receive the checkcode");
763                        checkcode_sender
764                            .send(checkcode)
765                            .await
766                            .expect("Alice should be able to forward the checkcode");
767                    }
768                    GrantLoginProgress::WaitingForAuth { verification_uri } => {
769                        assert_matches!(
770                            state,
771                            GrantLoginProgress::EstablishingSecureChannel(
772                                GeneratedQrProgress::QrScanned(_)
773                            )
774                        );
775                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
776                    }
777                    GrantLoginProgress::SyncingSecrets => {
778                        assert_matches!(state, GrantLoginProgress::WaitingForAuth { .. });
779                    }
780                    GrantLoginProgress::Done => {
781                        assert_matches!(state, GrantLoginProgress::SyncingSecrets);
782                        break;
783                    }
784                }
785                state = update;
786            }
787        });
788
789        // Let Bob request the login and run through the process.
790        let bob_task = spawn(async move {
791            request_login_with_scanned_qr_code(
792                BobBehaviour::HappyPath,
793                qr_code_rx,
794                checkcode_tx,
795                server,
796                rendezvous_server,
797                Some(device_authorization_grant),
798                Some(secrets_bundle),
799            )
800            .await;
801        });
802
803        // Wait for all tasks to finish.
804        grant.await.expect("Alice should be able to grant the login");
805        updates_task.await.expect("Alice should run through all progress states");
806        bob_task.await.expect("Bob's task should finish");
807    }
808
809    #[async_test]
810    async fn test_grant_login_with_scanned_qr_code() {
811        let server = MatrixMockServer::new().await;
812        let rendezvous_server =
813            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
814        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
815
816        let device_authorization_grant = AuthorizationGrant {
817            verification_uri_complete: Some(VerificationUriComplete::new(
818                "https://id.matrix.org/device/abcde".to_owned(),
819            )),
820            verification_uri: EndUserVerificationUrl::new(
821                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
822            )
823            .unwrap(),
824        };
825
826        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
827        server
828            .mock_upload_cross_signing_keys()
829            .ok()
830            .expect(1)
831            .named("upload_xsigning_keys")
832            .mount()
833            .await;
834        server
835            .mock_upload_cross_signing_signatures()
836            .ok()
837            .expect(1)
838            .named("upload_xsigning_signatures")
839            .mount()
840            .await;
841
842        // Create a secure channel on the new client (Bob) and extract the QR code.
843        let client = HttpClient::new(reqwest::Client::new(), Default::default());
844        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
845            .await
846            .expect("Bob should be able to create a secure channel.");
847        let qr_code_data = channel.qr_code_data().clone();
848
849        // Create the existing client (Alice).
850        let user_id = owned_user_id!("@alice:example.org");
851        let device_id = owned_device_id!("ALICE_DEVICE");
852        let alice = server
853            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
854            .logged_in_with_oauth()
855            .build()
856            .await;
857        alice
858            .encryption()
859            .bootstrap_cross_signing(None)
860            .await
861            .expect("Alice should be able to set up cross signing");
862
863        // Prepare the login granting future using the QR code.
864        let oauth = alice.oauth();
865        let grant = oauth
866            .grant_login_with_qr_code()
867            .device_creation_timeout(Duration::from_secs(2))
868            .scan(&qr_code_data);
869        let secrets_bundle = export_secrets_bundle(&alice)
870            .await
871            .expect("Alice should be able to export the secrets bundle");
872        let (checkcode_tx, checkcode_rx) = oneshot::channel();
873
874        // Spawn the updates task.
875        let mut updates = grant.subscribe_to_progress();
876        let mut state = grant.state.get();
877        let verification_uri_complete =
878            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
879        assert_matches!(state.clone(), GrantLoginProgress::Starting);
880        let updates_task = spawn(async move {
881            let mut checkcode_tx = Some(checkcode_tx);
882
883            while let Some(update) = updates.next().await {
884                match &update {
885                    GrantLoginProgress::Starting => {
886                        assert_matches!(state, GrantLoginProgress::Starting);
887                    }
888                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
889                        assert_matches!(state, GrantLoginProgress::Starting);
890                        checkcode_tx
891                            .take()
892                            .expect("The checkcode should only be forwarded once")
893                            .send(check_code.to_digit())
894                            .expect("Alice should be able to forward the checkcode");
895                    }
896                    GrantLoginProgress::WaitingForAuth { verification_uri } => {
897                        assert_matches!(
898                            state,
899                            GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
900                        );
901                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
902                    }
903                    GrantLoginProgress::SyncingSecrets => {
904                        assert_matches!(state, GrantLoginProgress::WaitingForAuth { .. });
905                    }
906                    GrantLoginProgress::Done => {
907                        assert_matches!(state, GrantLoginProgress::SyncingSecrets);
908                        break;
909                    }
910                }
911                state = update;
912            }
913        });
914
915        // Let Bob request the login and run through the process.
916        let bob_task = spawn(async move {
917            request_login_with_generated_qr_code(
918                BobBehaviour::HappyPath,
919                channel,
920                checkcode_rx,
921                server,
922                rendezvous_server,
923                alice.homeserver(),
924                Some(device_authorization_grant),
925                Some(secrets_bundle),
926            )
927            .await;
928        });
929
930        // Wait for all tasks to finish.
931        grant.await.expect("Alice should be able to grant the login");
932        updates_task.await.expect("Alice should run through all progress states");
933        bob_task.await.expect("Bob's task should finish");
934    }
935
936    #[async_test]
937    async fn test_grant_login_with_scanned_qr_code_with_homeserver_swap() {
938        let server = MatrixMockServer::new().await;
939        let rendezvous_server =
940            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
941        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
942
943        let device_authorization_grant = AuthorizationGrant {
944            verification_uri_complete: Some(VerificationUriComplete::new(
945                "https://id.matrix.org/device/abcde".to_owned(),
946            )),
947            verification_uri: EndUserVerificationUrl::new(
948                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
949            )
950            .unwrap(),
951        };
952
953        let login_server = MatrixMockServer::new().await;
954
955        login_server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
956        login_server
957            .mock_upload_cross_signing_keys()
958            .ok()
959            .expect(1)
960            .named("upload_xsigning_keys")
961            .mount()
962            .await;
963        login_server
964            .mock_upload_cross_signing_signatures()
965            .ok()
966            .expect(1)
967            .named("upload_xsigning_signatures")
968            .mount()
969            .await;
970
971        // Create a secure channel on the new client (Bob) and extract the QR code.
972        let client = HttpClient::new(reqwest::Client::new(), Default::default());
973        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
974            .await
975            .expect("Bob should be able to create a secure channel.");
976        let qr_code_data = channel.qr_code_data().clone();
977
978        // Create the existing client (Alice).
979        let user_id = owned_user_id!("@alice:example.org");
980        let device_id = owned_device_id!("ALICE_DEVICE");
981        let alice = login_server
982            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
983            .logged_in_with_oauth()
984            .build()
985            .await;
986        alice
987            .encryption()
988            .bootstrap_cross_signing(None)
989            .await
990            .expect("Alice should be able to set up cross signing");
991
992        // Prepare the login granting future using the QR code.
993        let oauth = alice.oauth();
994        let grant = oauth
995            .grant_login_with_qr_code()
996            .device_creation_timeout(Duration::from_secs(2))
997            .scan(&qr_code_data);
998        let secrets_bundle = export_secrets_bundle(&alice)
999            .await
1000            .expect("Alice should be able to export the secrets bundle");
1001        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1002
1003        // Spawn the updates task.
1004        let mut updates = grant.subscribe_to_progress();
1005        let mut state = grant.state.get();
1006        let verification_uri_complete =
1007            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
1008        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1009        let updates_task = spawn(async move {
1010            let mut checkcode_tx = Some(checkcode_tx);
1011
1012            while let Some(update) = updates.next().await {
1013                match &update {
1014                    GrantLoginProgress::Starting => {
1015                        assert_matches!(state, GrantLoginProgress::Starting);
1016                    }
1017                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1018                        assert_matches!(state, GrantLoginProgress::Starting);
1019                        checkcode_tx
1020                            .take()
1021                            .expect("The checkcode should only be forwarded once")
1022                            .send(check_code.to_digit())
1023                            .expect("Alice should be able to forward the checkcode");
1024                    }
1025                    GrantLoginProgress::WaitingForAuth { verification_uri } => {
1026                        assert_matches!(
1027                            state,
1028                            GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
1029                        );
1030                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
1031                    }
1032                    GrantLoginProgress::SyncingSecrets => {
1033                        assert_matches!(state, GrantLoginProgress::WaitingForAuth { .. });
1034                    }
1035                    GrantLoginProgress::Done => {
1036                        assert_matches!(state, GrantLoginProgress::SyncingSecrets);
1037                        break;
1038                    }
1039                }
1040                state = update;
1041            }
1042        });
1043
1044        // Let Bob request the login and run through the process.
1045        let bob_task = spawn(async move {
1046            request_login_with_generated_qr_code(
1047                BobBehaviour::HappyPath,
1048                channel,
1049                checkcode_rx,
1050                login_server,
1051                rendezvous_server,
1052                alice.homeserver(),
1053                Some(device_authorization_grant),
1054                Some(secrets_bundle),
1055            )
1056            .await;
1057        });
1058
1059        // Wait for all tasks to finish.
1060        grant.await.expect("Alice should be able to grant the login");
1061        updates_task.await.expect("Alice should run through all progress states");
1062        bob_task.await.expect("Bob's task should finish");
1063    }
1064
1065    #[async_test]
1066    async fn test_grant_login_with_generated_qr_code_unexpected_message_instead_of_login_protocol()
1067    {
1068        let server = MatrixMockServer::new().await;
1069        let rendezvous_server =
1070            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1071        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1072
1073        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1074        server
1075            .mock_upload_cross_signing_keys()
1076            .ok()
1077            .expect(1)
1078            .named("upload_xsigning_keys")
1079            .mount()
1080            .await;
1081        server
1082            .mock_upload_cross_signing_signatures()
1083            .ok()
1084            .expect(1)
1085            .named("upload_xsigning_signatures")
1086            .mount()
1087            .await;
1088
1089        // Create the existing client (Alice).
1090        let user_id = owned_user_id!("@alice:example.org");
1091        let device_id = owned_device_id!("ALICE_DEVICE");
1092        let alice = server
1093            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1094            .logged_in_with_oauth()
1095            .build()
1096            .await;
1097        alice
1098            .encryption()
1099            .bootstrap_cross_signing(None)
1100            .await
1101            .expect("Alice should be able to set up cross signing");
1102
1103        // Prepare the login granting future.
1104        let oauth = alice.oauth();
1105        let grant = oauth
1106            .grant_login_with_qr_code()
1107            .device_creation_timeout(Duration::from_secs(2))
1108            .generate();
1109        let (qr_code_tx, qr_code_rx) = oneshot::channel();
1110        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1111
1112        // Spawn the updates task.
1113        let mut updates = grant.subscribe_to_progress();
1114        let mut state = grant.state.get();
1115        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1116        let updates_task = spawn(async move {
1117            let mut qr_code_tx = Some(qr_code_tx);
1118            let mut checkcode_rx = Some(checkcode_rx);
1119
1120            while let Some(update) = updates.next().await {
1121                match &update {
1122                    GrantLoginProgress::Starting => {
1123                        assert_matches!(state, GrantLoginProgress::Starting);
1124                    }
1125                    GrantLoginProgress::EstablishingSecureChannel(
1126                        GeneratedQrProgress::QrReady(qr_code_data),
1127                    ) => {
1128                        assert_matches!(state, GrantLoginProgress::Starting);
1129                        qr_code_tx
1130                            .take()
1131                            .expect("The QR code should only be forwarded once")
1132                            .send(qr_code_data.clone())
1133                            .expect("Alice should be able to forward the QR code");
1134                    }
1135                    GrantLoginProgress::EstablishingSecureChannel(
1136                        GeneratedQrProgress::QrScanned(checkcode_sender),
1137                    ) => {
1138                        assert_matches!(
1139                            state,
1140                            GrantLoginProgress::EstablishingSecureChannel(
1141                                GeneratedQrProgress::QrReady(_)
1142                            )
1143                        );
1144                        let checkcode = checkcode_rx
1145                            .take()
1146                            .expect("The checkcode should only be forwarded once")
1147                            .await
1148                            .expect("Alice should receive the checkcode");
1149                        checkcode_sender
1150                            .send(checkcode)
1151                            .await
1152                            .expect("Alice should be able to forward the checkcode");
1153                        break;
1154                    }
1155                    _ => {
1156                        panic!("Alice should abort the process");
1157                    }
1158                }
1159                state = update;
1160            }
1161        });
1162
1163        // Let Bob request the login and run through the process.
1164        let bob_task = spawn(async move {
1165            request_login_with_scanned_qr_code(
1166                BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol,
1167                qr_code_rx,
1168                checkcode_tx,
1169                server,
1170                rendezvous_server,
1171                None,
1172                None,
1173            )
1174            .await;
1175        });
1176
1177        // Wait for all tasks to finish / fail.
1178        grant.await.expect_err("Alice should abort the login");
1179        updates_task.await.expect("Alice should run through all progress states");
1180        bob_task.await.expect("Bob's task should finish");
1181    }
1182
1183    #[async_test]
1184    async fn test_grant_login_with_scanned_qr_code_unexpected_message_instead_of_login_protocol() {
1185        let server = MatrixMockServer::new().await;
1186        let rendezvous_server =
1187            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1188        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1189
1190        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1191        server
1192            .mock_upload_cross_signing_keys()
1193            .ok()
1194            .expect(1)
1195            .named("upload_xsigning_keys")
1196            .mount()
1197            .await;
1198        server
1199            .mock_upload_cross_signing_signatures()
1200            .ok()
1201            .expect(1)
1202            .named("upload_xsigning_signatures")
1203            .mount()
1204            .await;
1205
1206        // Create a secure channel on the new client (Bob) and extract the QR code.
1207        let client = HttpClient::new(reqwest::Client::new(), Default::default());
1208        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1209            .await
1210            .expect("Bob should be able to create a secure channel.");
1211        let qr_code_data = channel.qr_code_data().clone();
1212
1213        // Create the existing client (Alice).
1214        let user_id = owned_user_id!("@alice:example.org");
1215        let device_id = owned_device_id!("ALICE_DEVICE");
1216        let alice = server
1217            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1218            .logged_in_with_oauth()
1219            .build()
1220            .await;
1221        alice
1222            .encryption()
1223            .bootstrap_cross_signing(None)
1224            .await
1225            .expect("Alice should be able to set up cross signing");
1226
1227        // Prepare the login granting future using the QR code.
1228        let oauth = alice.oauth();
1229        let grant = oauth
1230            .grant_login_with_qr_code()
1231            .device_creation_timeout(Duration::from_secs(2))
1232            .scan(&qr_code_data);
1233        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1234
1235        // Spawn the updates task.
1236        let mut updates = grant.subscribe_to_progress();
1237        let mut state = grant.state.get();
1238        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1239        let updates_task = spawn(async move {
1240            let mut checkcode_tx = Some(checkcode_tx);
1241
1242            while let Some(update) = updates.next().await {
1243                match &update {
1244                    GrantLoginProgress::Starting => {
1245                        assert_matches!(state, GrantLoginProgress::Starting);
1246                    }
1247                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1248                        assert_matches!(state, GrantLoginProgress::Starting);
1249                        checkcode_tx
1250                            .take()
1251                            .expect("The checkcode should only be forwarded once")
1252                            .send(check_code.to_digit())
1253                            .expect("Alice should be able to forward the checkcode");
1254                        break;
1255                    }
1256                    _ => {
1257                        panic!("Alice should abort the process");
1258                    }
1259                }
1260                state = update;
1261            }
1262        });
1263
1264        // Let Bob request the login and run through the process.
1265        let bob_task = spawn(async move {
1266            request_login_with_generated_qr_code(
1267                BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol,
1268                channel,
1269                checkcode_rx,
1270                server,
1271                rendezvous_server,
1272                alice.homeserver(),
1273                None,
1274                None,
1275            )
1276            .await;
1277        });
1278
1279        grant.await.expect_err("Alice should abort the login");
1280
1281        // Wait for all tasks to finish / fail.
1282        updates_task.await.expect("Alice should run through all progress states");
1283        bob_task.await.expect("Bob's task should finish");
1284    }
1285
1286    #[async_test]
1287    async fn test_grant_login_with_generated_qr_code_device_already_exists() {
1288        let server = MatrixMockServer::new().await;
1289        let rendezvous_server =
1290            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1291        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1292
1293        let device_authorization_grant = AuthorizationGrant {
1294            verification_uri_complete: Some(VerificationUriComplete::new(
1295                "https://id.matrix.org/device/abcde".to_owned(),
1296            )),
1297            verification_uri: EndUserVerificationUrl::new(
1298                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1299            )
1300            .unwrap(),
1301        };
1302
1303        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1304        server
1305            .mock_upload_cross_signing_keys()
1306            .ok()
1307            .expect(1)
1308            .named("upload_xsigning_keys")
1309            .mount()
1310            .await;
1311        server
1312            .mock_upload_cross_signing_signatures()
1313            .ok()
1314            .expect(1)
1315            .named("upload_xsigning_signatures")
1316            .mount()
1317            .await;
1318
1319        // Create the existing client (Alice).
1320        let user_id = owned_user_id!("@alice:example.org");
1321        let device_id = owned_device_id!("ALICE_DEVICE");
1322        let alice = server
1323            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1324            .logged_in_with_oauth()
1325            .build()
1326            .await;
1327        alice
1328            .encryption()
1329            .bootstrap_cross_signing(None)
1330            .await
1331            .expect("Alice should be able to set up cross signing");
1332
1333        // Prepare the login granting future.
1334        let oauth = alice.oauth();
1335        let grant = oauth
1336            .grant_login_with_qr_code()
1337            .device_creation_timeout(Duration::from_secs(2))
1338            .generate();
1339        let (qr_code_tx, qr_code_rx) = oneshot::channel();
1340        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1341
1342        // Spawn the updates task.
1343        let mut updates = grant.subscribe_to_progress();
1344        let mut state = grant.state.get();
1345        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1346        let updates_task = spawn(async move {
1347            let mut qr_code_tx = Some(qr_code_tx);
1348            let mut checkcode_rx = Some(checkcode_rx);
1349
1350            while let Some(update) = updates.next().await {
1351                match &update {
1352                    GrantLoginProgress::Starting => {
1353                        assert_matches!(state, GrantLoginProgress::Starting);
1354                    }
1355                    GrantLoginProgress::EstablishingSecureChannel(
1356                        GeneratedQrProgress::QrReady(qr_code_data),
1357                    ) => {
1358                        assert_matches!(state, GrantLoginProgress::Starting);
1359                        qr_code_tx
1360                            .take()
1361                            .expect("The QR code should only be forwarded once")
1362                            .send(qr_code_data.clone())
1363                            .expect("Alice should be able to forward the QR code");
1364                    }
1365                    GrantLoginProgress::EstablishingSecureChannel(
1366                        GeneratedQrProgress::QrScanned(checkcode_sender),
1367                    ) => {
1368                        assert_matches!(
1369                            state,
1370                            GrantLoginProgress::EstablishingSecureChannel(
1371                                GeneratedQrProgress::QrReady(_)
1372                            )
1373                        );
1374                        let checkcode = checkcode_rx
1375                            .take()
1376                            .expect("The checkcode should only be forwarded once")
1377                            .await
1378                            .expect("Alice should receive the checkcode");
1379                        checkcode_sender
1380                            .send(checkcode)
1381                            .await
1382                            .expect("Alice should be able to forward the checkcode");
1383                    }
1384                    _ => {
1385                        panic!("Alice should abort the process");
1386                    }
1387                }
1388                state = update;
1389            }
1390        });
1391
1392        // Let Bob request the login and run through the process.
1393        let bob_task = spawn(async move {
1394            request_login_with_scanned_qr_code(
1395                BobBehaviour::DeviceAlreadyExists,
1396                qr_code_rx,
1397                checkcode_tx,
1398                server,
1399                rendezvous_server,
1400                Some(device_authorization_grant),
1401                None,
1402            )
1403            .await;
1404        });
1405
1406        // Wait for all tasks to finish.
1407        grant.await.expect_err("Alice should abort the login");
1408        updates_task.await.expect("Alice should run through all progress states");
1409        bob_task.await.expect("Bob's task should finish");
1410    }
1411
1412    #[async_test]
1413    async fn test_grant_login_with_scanned_qr_code_device_already_exists() {
1414        let server = MatrixMockServer::new().await;
1415        let rendezvous_server =
1416            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1417        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1418
1419        let device_authorization_grant = AuthorizationGrant {
1420            verification_uri_complete: Some(VerificationUriComplete::new(
1421                "https://id.matrix.org/device/abcde".to_owned(),
1422            )),
1423            verification_uri: EndUserVerificationUrl::new(
1424                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1425            )
1426            .unwrap(),
1427        };
1428
1429        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1430        server
1431            .mock_upload_cross_signing_keys()
1432            .ok()
1433            .expect(1)
1434            .named("upload_xsigning_keys")
1435            .mount()
1436            .await;
1437        server
1438            .mock_upload_cross_signing_signatures()
1439            .ok()
1440            .expect(1)
1441            .named("upload_xsigning_signatures")
1442            .mount()
1443            .await;
1444
1445        // Create a secure channel on the new client (Bob) and extract the QR code.
1446        let client = HttpClient::new(reqwest::Client::new(), Default::default());
1447        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1448            .await
1449            .expect("Bob should be able to create a secure channel.");
1450        let qr_code_data = channel.qr_code_data().clone();
1451
1452        // Create the existing client (Alice).
1453        let user_id = owned_user_id!("@alice:example.org");
1454        let device_id = owned_device_id!("ALICE_DEVICE");
1455        let alice = server
1456            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1457            .logged_in_with_oauth()
1458            .build()
1459            .await;
1460        alice
1461            .encryption()
1462            .bootstrap_cross_signing(None)
1463            .await
1464            .expect("Alice should be able to set up cross signing");
1465
1466        // Prepare the login granting future using the QR code.
1467        let oauth = alice.oauth();
1468        let grant = oauth
1469            .grant_login_with_qr_code()
1470            .device_creation_timeout(Duration::from_secs(2))
1471            .scan(&qr_code_data);
1472        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1473
1474        // Spawn the updates task.
1475        let mut updates = grant.subscribe_to_progress();
1476        let mut state = grant.state.get();
1477        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1478        let updates_task = spawn(async move {
1479            let mut checkcode_tx = Some(checkcode_tx);
1480
1481            while let Some(update) = updates.next().await {
1482                match &update {
1483                    GrantLoginProgress::Starting => {
1484                        assert_matches!(state, GrantLoginProgress::Starting);
1485                    }
1486                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1487                        assert_matches!(state, GrantLoginProgress::Starting);
1488                        checkcode_tx
1489                            .take()
1490                            .expect("The checkcode should only be forwarded once")
1491                            .send(check_code.to_digit())
1492                            .expect("Alice should be able to forward the checkcode");
1493                    }
1494                    _ => {
1495                        panic!("Alice should abort the process");
1496                    }
1497                }
1498                state = update;
1499            }
1500        });
1501
1502        // Let Bob request the login and run through the process.
1503        let bob_task = spawn(async move {
1504            request_login_with_generated_qr_code(
1505                BobBehaviour::DeviceAlreadyExists,
1506                channel,
1507                checkcode_rx,
1508                server,
1509                rendezvous_server,
1510                alice.homeserver(),
1511                Some(device_authorization_grant),
1512                None,
1513            )
1514            .await;
1515        });
1516
1517        // Wait for all tasks to finish.
1518        grant.await.expect_err("Alice should abort the login");
1519        updates_task.await.expect("Alice should run through all progress states");
1520        bob_task.await.expect("Bob's task should finish");
1521    }
1522
1523    #[async_test]
1524    async fn test_grant_login_with_generated_qr_code_device_not_created() {
1525        let server = MatrixMockServer::new().await;
1526        let rendezvous_server =
1527            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1528        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1529
1530        let device_authorization_grant = AuthorizationGrant {
1531            verification_uri_complete: Some(VerificationUriComplete::new(
1532                "https://id.matrix.org/device/abcde".to_owned(),
1533            )),
1534            verification_uri: EndUserVerificationUrl::new(
1535                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1536            )
1537            .unwrap(),
1538        };
1539
1540        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1541        server
1542            .mock_upload_cross_signing_keys()
1543            .ok()
1544            .expect(1)
1545            .named("upload_xsigning_keys")
1546            .mount()
1547            .await;
1548        server
1549            .mock_upload_cross_signing_signatures()
1550            .ok()
1551            .expect(1)
1552            .named("upload_xsigning_signatures")
1553            .mount()
1554            .await;
1555
1556        // Create the existing client (Alice).
1557        let user_id = owned_user_id!("@alice:example.org");
1558        let device_id = owned_device_id!("ALICE_DEVICE");
1559        let alice = server
1560            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1561            .logged_in_with_oauth()
1562            .build()
1563            .await;
1564        alice
1565            .encryption()
1566            .bootstrap_cross_signing(None)
1567            .await
1568            .expect("Alice should be able to set up cross signing");
1569
1570        // Prepare the login granting future.
1571        let oauth = alice.oauth();
1572        let grant = oauth
1573            .grant_login_with_qr_code()
1574            .device_creation_timeout(Duration::from_secs(2))
1575            .generate();
1576        let (qr_code_tx, qr_code_rx) = oneshot::channel();
1577        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1578
1579        // Spawn the updates task.
1580        let mut updates = grant.subscribe_to_progress();
1581        let mut state = grant.state.get();
1582        let verification_uri_complete =
1583            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
1584        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1585        let updates_task = spawn(async move {
1586            let mut qr_code_tx = Some(qr_code_tx);
1587            let mut checkcode_rx = Some(checkcode_rx);
1588
1589            while let Some(update) = updates.next().await {
1590                match &update {
1591                    GrantLoginProgress::Starting => {
1592                        assert_matches!(state, GrantLoginProgress::Starting);
1593                    }
1594                    GrantLoginProgress::EstablishingSecureChannel(
1595                        GeneratedQrProgress::QrReady(qr_code_data),
1596                    ) => {
1597                        assert_matches!(state, GrantLoginProgress::Starting);
1598                        qr_code_tx
1599                            .take()
1600                            .expect("The QR code should only be forwarded once")
1601                            .send(qr_code_data.clone())
1602                            .expect("Alice should be able to forward the QR code");
1603                    }
1604                    GrantLoginProgress::EstablishingSecureChannel(
1605                        GeneratedQrProgress::QrScanned(checkcode_sender),
1606                    ) => {
1607                        assert_matches!(
1608                            state,
1609                            GrantLoginProgress::EstablishingSecureChannel(
1610                                GeneratedQrProgress::QrReady(_)
1611                            )
1612                        );
1613                        let checkcode = checkcode_rx
1614                            .take()
1615                            .expect("The checkcode should only be forwarded once")
1616                            .await
1617                            .expect("Alice should receive the checkcode");
1618                        checkcode_sender
1619                            .send(checkcode)
1620                            .await
1621                            .expect("Alice should be able to forward the checkcode");
1622                    }
1623                    GrantLoginProgress::WaitingForAuth { verification_uri } => {
1624                        assert_matches!(
1625                            state,
1626                            GrantLoginProgress::EstablishingSecureChannel(
1627                                GeneratedQrProgress::QrScanned(_)
1628                            )
1629                        );
1630                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
1631                    }
1632                    _ => {
1633                        panic!("Alice should abort the process");
1634                    }
1635                }
1636                state = update;
1637            }
1638        });
1639
1640        // Let Bob request the login and run through the process.
1641        let bob_task = spawn(async move {
1642            request_login_with_scanned_qr_code(
1643                BobBehaviour::DeviceNotCreated,
1644                qr_code_rx,
1645                checkcode_tx,
1646                server,
1647                rendezvous_server,
1648                Some(device_authorization_grant),
1649                None,
1650            )
1651            .await;
1652        });
1653
1654        grant.await.expect_err("Alice should abort the login");
1655        updates_task.await.expect("Alice should run through all progress states");
1656        bob_task.await.expect("Bob's task should finish");
1657    }
1658
1659    #[async_test]
1660    async fn test_grant_login_with_scanned_qr_code_device_not_created() {
1661        let server = MatrixMockServer::new().await;
1662        let rendezvous_server =
1663            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1664        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1665
1666        let device_authorization_grant = AuthorizationGrant {
1667            verification_uri_complete: Some(VerificationUriComplete::new(
1668                "https://id.matrix.org/device/abcde".to_owned(),
1669            )),
1670            verification_uri: EndUserVerificationUrl::new(
1671                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1672            )
1673            .unwrap(),
1674        };
1675
1676        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1677        server
1678            .mock_upload_cross_signing_keys()
1679            .ok()
1680            .expect(1)
1681            .named("upload_xsigning_keys")
1682            .mount()
1683            .await;
1684        server
1685            .mock_upload_cross_signing_signatures()
1686            .ok()
1687            .expect(1)
1688            .named("upload_xsigning_signatures")
1689            .mount()
1690            .await;
1691
1692        // Create a secure channel on the new client (Bob) and extract the QR code.
1693        let client = HttpClient::new(reqwest::Client::new(), Default::default());
1694        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1695            .await
1696            .expect("Bob should be able to create a secure channel.");
1697        let qr_code_data = channel.qr_code_data().clone();
1698
1699        // Create the existing client (Alice).
1700        let user_id = owned_user_id!("@alice:example.org");
1701        let device_id = owned_device_id!("ALICE_DEVICE");
1702        let alice = server
1703            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1704            .logged_in_with_oauth()
1705            .build()
1706            .await;
1707        alice
1708            .encryption()
1709            .bootstrap_cross_signing(None)
1710            .await
1711            .expect("Alice should be able to set up cross signing");
1712
1713        // Prepare the login granting future using the QR code.
1714        let oauth = alice.oauth();
1715        let grant = oauth
1716            .grant_login_with_qr_code()
1717            .device_creation_timeout(Duration::from_secs(2))
1718            .scan(&qr_code_data);
1719        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1720
1721        // Spawn the updates task.
1722        let mut updates = grant.subscribe_to_progress();
1723        let mut state = grant.state.get();
1724        let verification_uri_complete =
1725            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
1726        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1727        let updates_task = spawn(async move {
1728            let mut checkcode_tx = Some(checkcode_tx);
1729
1730            while let Some(update) = updates.next().await {
1731                match &update {
1732                    GrantLoginProgress::Starting => {
1733                        assert_matches!(state, GrantLoginProgress::Starting);
1734                    }
1735                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1736                        assert_matches!(state, GrantLoginProgress::Starting);
1737                        checkcode_tx
1738                            .take()
1739                            .expect("The checkcode should only be forwarded once")
1740                            .send(check_code.to_digit())
1741                            .expect("Alice should be able to forward the checkcode");
1742                    }
1743                    GrantLoginProgress::WaitingForAuth { verification_uri } => {
1744                        assert_matches!(
1745                            state,
1746                            GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
1747                        );
1748                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
1749                    }
1750                    _ => {
1751                        panic!("Alice should abort the process");
1752                    }
1753                }
1754                state = update;
1755            }
1756        });
1757
1758        // Let Bob request the login and run through the process.
1759        let bob_task = spawn(async move {
1760            request_login_with_generated_qr_code(
1761                BobBehaviour::DeviceNotCreated,
1762                channel,
1763                checkcode_rx,
1764                server,
1765                rendezvous_server,
1766                alice.homeserver(),
1767                Some(device_authorization_grant),
1768                None,
1769            )
1770            .await;
1771        });
1772
1773        grant.await.expect_err("Alice should abort the login");
1774        updates_task.await.expect("Alice should run through all progress states");
1775        bob_task.await.expect("Bob's task should finish");
1776    }
1777
1778    #[async_test]
1779    async fn test_grant_login_with_generated_qr_code_session_expired() {
1780        let server = MatrixMockServer::new().await;
1781        let rendezvous_server =
1782            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::from_secs(2))
1783                .await;
1784        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1785
1786        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1787        server
1788            .mock_upload_cross_signing_keys()
1789            .ok()
1790            .expect(1)
1791            .named("upload_xsigning_keys")
1792            .mount()
1793            .await;
1794        server
1795            .mock_upload_cross_signing_signatures()
1796            .ok()
1797            .expect(1)
1798            .named("upload_xsigning_signatures")
1799            .mount()
1800            .await;
1801
1802        // Create the existing client (Alice).
1803        let user_id = owned_user_id!("@alice:example.org");
1804        let device_id = owned_device_id!("ALICE_DEVICE");
1805        let alice = server
1806            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1807            .logged_in_with_oauth()
1808            .build()
1809            .await;
1810        alice
1811            .encryption()
1812            .bootstrap_cross_signing(None)
1813            .await
1814            .expect("Alice should be able to set up cross signing");
1815
1816        // Prepare the login granting future.
1817        let oauth = alice.oauth();
1818        let grant = oauth
1819            .grant_login_with_qr_code()
1820            .device_creation_timeout(Duration::from_secs(2))
1821            .generate();
1822
1823        // Spawn the updates task.
1824        let mut updates = grant.subscribe_to_progress();
1825        let mut state = grant.state.get();
1826        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1827        let updates_task = spawn(async move {
1828            while let Some(update) = updates.next().await {
1829                match &update {
1830                    GrantLoginProgress::Starting => {
1831                        assert_matches!(state, GrantLoginProgress::Starting);
1832                    }
1833                    GrantLoginProgress::EstablishingSecureChannel(
1834                        GeneratedQrProgress::QrReady(_),
1835                    ) => {
1836                        assert_matches!(state, GrantLoginProgress::Starting);
1837                    }
1838                    _ => {
1839                        panic!("Alice should abort the process");
1840                    }
1841                }
1842                state = update;
1843            }
1844        });
1845
1846        // Bob does not scan the QR code and the channel is never connected.
1847
1848        // Wait for the rendezvous session to time out.
1849        assert_matches!(grant.await, Err(QRCodeGrantLoginError::NotFound));
1850        updates_task.await.expect("Alice should run through all progress states");
1851    }
1852
1853    #[async_test]
1854    async fn test_grant_login_with_scanned_qr_code_session_expired() {
1855        let server = MatrixMockServer::new().await;
1856        let rendezvous_server =
1857            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::from_secs(2))
1858                .await;
1859        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1860
1861        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1862        server
1863            .mock_upload_cross_signing_keys()
1864            .ok()
1865            .expect(1)
1866            .named("upload_xsigning_keys")
1867            .mount()
1868            .await;
1869        server
1870            .mock_upload_cross_signing_signatures()
1871            .ok()
1872            .expect(1)
1873            .named("upload_xsigning_signatures")
1874            .mount()
1875            .await;
1876
1877        // Create a secure channel on the new client (Bob) and extract the QR code.
1878        let client = HttpClient::new(reqwest::Client::new(), Default::default());
1879        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1880            .await
1881            .expect("Bob should be able to create a secure channel.");
1882        let qr_code_data = channel.qr_code_data().clone();
1883
1884        // Create the existing client (Alice).
1885        let user_id = owned_user_id!("@alice:example.org");
1886        let device_id = owned_device_id!("ALICE_DEVICE");
1887        let alice = server
1888            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1889            .logged_in_with_oauth()
1890            .build()
1891            .await;
1892        alice
1893            .encryption()
1894            .bootstrap_cross_signing(None)
1895            .await
1896            .expect("Alice should be able to set up cross signing");
1897
1898        // Prepare the login granting future using the QR code.
1899        let oauth = alice.oauth();
1900        let grant = oauth
1901            .grant_login_with_qr_code()
1902            .device_creation_timeout(Duration::from_secs(2))
1903            .scan(&qr_code_data);
1904
1905        // Spawn the updates task.
1906        let mut updates = grant.subscribe_to_progress();
1907        let mut state = grant.state.get();
1908        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1909        let updates_task = spawn(async move {
1910            while let Some(update) = updates.next().await {
1911                match &update {
1912                    GrantLoginProgress::Starting => {
1913                        assert_matches!(state, GrantLoginProgress::Starting);
1914                    }
1915                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. }) => {
1916                        assert_matches!(state, GrantLoginProgress::Starting);
1917                    }
1918                    _ => {
1919                        panic!("Alice should abort the process");
1920                    }
1921                }
1922                state = update;
1923            }
1924        });
1925
1926        // Bob does not connect the channel.
1927
1928        // Wait for the rendezvous session to time out.
1929        assert_matches!(grant.await, Err(QRCodeGrantLoginError::NotFound));
1930        updates_task.await.expect("Alice should run through all progress states");
1931    }
1932}