Skip to main content

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, CloneableSender, ContinuationMessage, ContinuationMessageSender,
40        GeneratedQrProgress, LoginFailureReason, QRCodeGrantLoginError, QrProgress,
41        SecureChannelError,
42    },
43};
44
45async fn export_secrets_bundle(client: &Client) -> Result<SecretsBundle, QRCodeGrantLoginError> {
46    let secrets_bundle = client
47        .olm_machine()
48        .await
49        .as_ref()
50        .ok_or_else(|| QRCodeGrantLoginError::MissingSecretsBackup(None))?
51        .store()
52        .export_secrets_bundle()
53        .await?;
54    Ok(secrets_bundle)
55}
56
57async fn finish_login_grant<Q>(
58    client: &Client,
59    channel: &mut EstablishedSecureChannel,
60    device_creation_timeout: Duration,
61    secrets_bundle: &SecretsBundle,
62    state: &SharedObservable<GrantLoginProgress<Q>>,
63) -> Result<(), QRCodeGrantLoginError> {
64    // The new device registers with the authorization server and sends it a
65    // device authorization authorization request.
66    //
67    // -- MSC4108 OAuth 2.0 login step 2
68
69    // We wait for the new device to send us the m.login.protocol message with
70    // the device authorization grant information.
71    //
72    // -- MSC4108 OAuth 2.0 login step 3
73    let (device_authorization_grant, protocol, device_id) = match channel.receive_json().await? {
74        QrAuthMessage::LoginProtocol { device_authorization_grant, protocol, device_id } => {
75            (device_authorization_grant, protocol, device_id)
76        }
77        QrAuthMessage::LoginFailure { reason, .. } => {
78            return Err(QRCodeGrantLoginError::LoginFailure { reason });
79        }
80        message => {
81            return Err(QRCodeGrantLoginError::UnexpectedMessage {
82                expected: "m.login.protocol",
83                received: Box::new(message),
84            });
85        }
86    };
87
88    // We verify the selected protocol.
89    //
90    // -- MSC4108 OAuth 2.0 login step 4
91    if protocol != LoginProtocolType::DeviceAuthorizationGrant {
92        channel
93            .send_json(QrAuthMessage::LoginFailure {
94                reason: LoginFailureReason::UnsupportedProtocol,
95                homeserver: None,
96            })
97            .await?;
98        return Err(QRCodeGrantLoginError::UnsupportedProtocol(protocol));
99    }
100
101    // We check that the device ID is still available.
102    //
103    // -- MSC4108 OAuth 2.0 login step 4 continued
104    if !matches!(client.device_exists(device_id.clone().into()).await, Ok(false)) {
105        channel
106            .send_json(QrAuthMessage::LoginFailure {
107                reason: LoginFailureReason::DeviceAlreadyExists,
108                homeserver: None,
109            })
110            .await?;
111        return Err(QRCodeGrantLoginError::DeviceIDAlreadyInUse);
112    }
113
114    // We emit an update so that the caller can open the verification URI in a
115    // system browser to consent to the login.
116    //
117    // -- MSC4108 OAuth 2.0 login step 4 continued
118    let verification_uri = Url::parse(
119        device_authorization_grant
120            .verification_uri_complete
121            .map(VerificationUriComplete::into_secret)
122            .unwrap_or(device_authorization_grant.verification_uri.to_string())
123            .as_str(),
124    )
125    .map_err(|e| QRCodeGrantLoginError::Unknown(e.to_string()))?;
126
127    let (sender, receiver) = tokio::sync::oneshot::channel();
128    state.set(GrantLoginProgress::WaitingForAuth {
129        verification_uri,
130        continuation_sender: ContinuationMessageSender(CloneableSender::new(sender)),
131    });
132
133    // We wait for this device to confirm that the authorization using the
134    // verification URI has succeeded.
135    match receiver.await {
136        Ok(ContinuationMessage::Confirm) => {}
137        Ok(ContinuationMessage::Cancel) | Err(_) => {
138            channel
139                .send_json(QrAuthMessage::LoginFailure {
140                    reason: LoginFailureReason::UserCancelled,
141                    homeserver: None,
142                })
143                .await?;
144            return Err(QRCodeGrantLoginError::LoginFailure {
145                reason: LoginFailureReason::UserCancelled,
146            });
147        }
148    }
149    // We send the new device the m.login.protocol_accepted message to let it
150    // know that the consent process is in progress.
151    //
152    // -- MSC4108 OAuth 2.0 login step 4 continued
153    let message = QrAuthMessage::LoginProtocolAccepted;
154    channel.send_json(&message).await?;
155
156    // The new device displays the user code it received from the authorization
157    // server and starts polling for an access token. In parallel, the user
158    // consents to the new login in the browser on this device, while verifying
159    // the user code displayed on the other device.
160    //
161    // -- MSC4108 OAuth 2.0 login steps 5 & 6
162
163    // We wait for the new device to send us the m.login.success or
164    // m.login.failure message
165    match channel.receive_json().await? {
166        QrAuthMessage::LoginSuccess => (),
167        QrAuthMessage::LoginFailure { reason, .. } => {
168            return Err(QRCodeGrantLoginError::LoginFailure { reason });
169        }
170        message => {
171            return Err(QRCodeGrantLoginError::UnexpectedMessage {
172                expected: "m.login.success",
173                received: Box::new(message),
174            });
175        }
176    }
177
178    // We check that the new device was created successfully, allowing for the
179    // specified delay.
180    //
181    // -- MSC4108 Secret sharing and device verification step 1
182    let deadline = Instant::now() + device_creation_timeout;
183
184    loop {
185        if matches!(client.device_exists(device_id.clone().into()).await, Ok(true)) {
186            break;
187        } else {
188            // If the deadline hasn't yet passed, give it some time and retry
189            // the request.
190            if Instant::now() < deadline {
191                matrix_sdk_common::sleep::sleep(Duration::from_millis(500)).await;
192                continue;
193            } else {
194                // The deadline has passed. Let's fail the login process.
195                channel
196                    .send_json(QrAuthMessage::LoginFailure {
197                        reason: LoginFailureReason::DeviceNotFound,
198                        homeserver: None,
199                    })
200                    .await?;
201                return Err(QRCodeGrantLoginError::DeviceNotFound);
202            }
203        }
204    }
205
206    // We send the new device the secrets bundle.
207    //
208    // -- MSC4108 Secret sharing and device verification step 2
209    state.set(GrantLoginProgress::SyncingSecrets);
210    let message = QrAuthMessage::LoginSecrets(secrets_bundle.clone());
211    channel.send_json(&message).await?;
212
213    // And we're done.
214    state.set(GrantLoginProgress::Done);
215
216    Ok(())
217}
218
219/// The progress of granting the login.
220#[derive(Clone, Debug, Default)]
221pub enum GrantLoginProgress<Q> {
222    /// We're just starting up, this is the default and initial state.
223    #[default]
224    Starting,
225    /// The secure channel is being established by exchanging the QR code and/or
226    /// [`CheckCode`].
227    EstablishingSecureChannel(Q),
228    /// The secure channel has been confirmed using the [`CheckCode`] and this
229    /// device is waiting for the authorization to complete.
230    WaitingForAuth {
231        /// A URI to open in a (secure) system browser to verify the new login.
232        verification_uri: Url,
233        /// A sender to confirm that the authorization using the verification
234        /// URI has been started in the browser and that the application is
235        /// ready to proceed. This allows applications that suspend or navigate
236        /// away while the verification URI is open to resume the process
237        /// explicitly.
238        continuation_sender: ContinuationMessageSender,
239    },
240    /// The new device has been granted access and this device is sending the
241    /// secrets to it.
242    SyncingSecrets,
243    /// The process is complete.
244    Done,
245}
246
247/// Named future for granting login by scanning a QR code on this, existing,
248/// device that was generated by the other, new, device.
249#[derive(Debug)]
250pub struct GrantLoginWithScannedQrCode<'a> {
251    client: &'a Client,
252    qr_code_data: &'a QrCodeData,
253    device_creation_timeout: Duration,
254    state: SharedObservable<GrantLoginProgress<QrProgress>>,
255}
256
257impl<'a> GrantLoginWithScannedQrCode<'a> {
258    pub(crate) fn new(
259        client: &'a Client,
260        qr_code_data: &'a QrCodeData,
261        device_creation_timeout: Duration,
262    ) -> GrantLoginWithScannedQrCode<'a> {
263        GrantLoginWithScannedQrCode {
264            client,
265            qr_code_data,
266            device_creation_timeout,
267            state: Default::default(),
268        }
269    }
270}
271
272impl GrantLoginWithScannedQrCode<'_> {
273    /// Subscribe to the progress of QR code login.
274    ///
275    /// It's necessary to subscribe to this to capture the [`CheckCode`] in
276    /// order to display it to the other device and to obtain the verification
277    /// URL for consenting to the login.
278    pub fn subscribe_to_progress(
279        &self,
280    ) -> impl Stream<Item = GrantLoginProgress<QrProgress>> + use<> {
281        self.state.subscribe()
282    }
283}
284
285impl<'a> IntoFuture for GrantLoginWithScannedQrCode<'a> {
286    type Output = Result<(), QRCodeGrantLoginError>;
287    boxed_into_future!(extra_bounds: 'a);
288
289    fn into_future(self) -> Self::IntoFuture {
290        Box::pin(async move {
291            // Before we get here, the other device has created a new rendezvous
292            // session and presented a QR code which this device has scanned.
293            //
294            // -- MSC4108 Secure channel setup steps 1-3
295
296            // First things first, export the secrets bundle and establish the
297            // secure channel. Since we're the one that scanned the QR code,
298            // we're certain that the secure channel is secure, under the
299            // assumption that we didn't scan the wrong QR code.
300            //
301            // -- MSC4108 Secure channel setup steps 3-5
302            let secrets_bundle = export_secrets_bundle(self.client).await?;
303
304            let mut channel = EstablishedSecureChannel::from_qr_code(
305                self.client.inner.http_client.inner.clone(),
306                self.qr_code_data,
307                QrCodeIntent::Reciprocate,
308            )
309            .await?;
310
311            // The other side isn't yet sure that it's talking to the right
312            // device, show a check code so they can confirm.
313            //
314            // -- MSC4108 Secure channel setup step 6
315            let check_code = channel.check_code().to_owned();
316            self.state
317                .set(GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }));
318
319            // The user now enters the checkcode on the other device which
320            // verifies it and will only continue requesting the login if the
321            // code matches.
322            //
323            // -- MSC4108 Secure channel setup step 7
324
325            // Inform the other device about the available login protocols and
326            // the homeserver to use.
327            //
328            // -- MSC4108 OAuth 2.0 login step 1
329            let message = QrAuthMessage::LoginProtocols {
330                protocols: vec![LoginProtocolType::DeviceAuthorizationGrant],
331                homeserver: self.client.homeserver(),
332            };
333            channel.send_json(message).await?;
334
335            // Proceed with granting the login.
336            //
337            // -- MSC4108 OAuth 2.0 login remaining steps
338            finish_login_grant(
339                self.client,
340                &mut channel,
341                self.device_creation_timeout,
342                &secrets_bundle,
343                &self.state,
344            )
345            .await
346        })
347    }
348}
349
350/// Named future for granting login by generating a QR code on this, existing,
351/// device to be scanned by the other, new, device.
352#[derive(Debug)]
353pub struct GrantLoginWithGeneratedQrCode<'a> {
354    client: &'a Client,
355    device_creation_timeout: Duration,
356    state: SharedObservable<GrantLoginProgress<GeneratedQrProgress>>,
357}
358
359impl<'a> GrantLoginWithGeneratedQrCode<'a> {
360    pub(crate) fn new(
361        client: &'a Client,
362        device_creation_timeout: Duration,
363    ) -> GrantLoginWithGeneratedQrCode<'a> {
364        GrantLoginWithGeneratedQrCode { client, device_creation_timeout, state: Default::default() }
365    }
366}
367
368impl GrantLoginWithGeneratedQrCode<'_> {
369    /// Subscribe to the progress of QR code login.
370    ///
371    /// It's necessary to subscribe to this to capture the QR code in order to
372    /// display it to the other device, to feed the [`CheckCode`] entered by the
373    /// user back in and to obtain the verification URL for consenting to the
374    /// login.
375    pub fn subscribe_to_progress(
376        &self,
377    ) -> impl Stream<Item = GrantLoginProgress<GeneratedQrProgress>> + use<> {
378        self.state.subscribe()
379    }
380}
381
382impl<'a> IntoFuture for GrantLoginWithGeneratedQrCode<'a> {
383    type Output = Result<(), QRCodeGrantLoginError>;
384    boxed_into_future!(extra_bounds: 'a);
385
386    fn into_future(self) -> Self::IntoFuture {
387        Box::pin(async move {
388            // Create a new ephemeral key pair and a rendezvous session to grant
389            // a login with.
390            //
391            // -- MSC4108 Secure channel setup steps 1 & 2
392            let homeserver_url = self.client.homeserver();
393            let http_client = self.client.inner.http_client.clone();
394            let secrets_bundle = export_secrets_bundle(self.client).await?;
395            let channel = SecureChannel::reciprocate(http_client, &homeserver_url).await?;
396
397            // Extract the QR code data and emit an update so that the caller
398            // can present the QR code for scanning by the new device.
399            //
400            // -- MSC4108 Secure channel setup step 3
401            self.state.set(GrantLoginProgress::EstablishingSecureChannel(
402                GeneratedQrProgress::QrReady(channel.qr_code_data().clone()),
403            ));
404
405            // Wait for the secure channel to connect. The other device now
406            // needs to scan the QR code and send us the LoginInitiateMessage
407            // which we respond to with the LoginOkMessage.
408            //
409            // -- MSC4108 step 4 & 5
410            let channel = channel.connect().await?;
411
412            // The other device now needs to verify our message, compute the
413            // checkcode and display it. We emit a progress update to let the
414            // caller prompt the user to enter the checkcode and feed it back to
415            // us.
416            //
417            // -- MSC4108 Secure channel setup step 6
418            let (tx, rx) = tokio::sync::oneshot::channel();
419            self.state.set(GrantLoginProgress::EstablishingSecureChannel(
420                GeneratedQrProgress::QrScanned(CheckCodeSender::new(tx)),
421            ));
422            let check_code = rx.await.map_err(|_| SecureChannelError::CannotReceiveCheckCode)?;
423
424            // Use the checkcode to verify that the channel is actually
425            // secure.
426            //
427            // -- MSC4108 Secure channel setup step 7
428            let mut channel = channel.confirm(check_code)?;
429
430            // Since the QR code was generated on this existing device, the new
431            // device can derive the homeserver to use for logging in from the
432            // QR code and we don't need to send the m.login.protocols
433            // message.
434            //
435            // -- MSC4108 OAuth 2.0 login step 1
436
437            // Proceed with granting the login.
438            //
439            // -- MSC4108 OAuth 2.0 login remaining steps
440            finish_login_grant(
441                self.client,
442                &mut channel,
443                self.device_creation_timeout,
444                &secrets_bundle,
445                &self.state,
446            )
447            .await
448        })
449    }
450}
451
452#[cfg(all(test, not(target_family = "wasm")))]
453mod test {
454    use std::{assert_matches, sync::Arc};
455
456    use futures_util::StreamExt;
457    use matrix_sdk_base::crypto::types::SecretsBundle;
458    use matrix_sdk_common::executor::spawn;
459    use matrix_sdk_test::async_test;
460    use oauth2::{EndUserVerificationUrl, VerificationUriComplete};
461    use ruma::{owned_device_id, owned_user_id};
462    use strass::assert_let;
463    use tokio::sync::oneshot;
464    use tracing::debug;
465
466    use super::*;
467    use crate::{
468        authentication::oauth::qrcode::{
469            LoginFailureReason, MessageDecodeError, QrAuthMessage,
470            messages::{AuthorizationGrant, LoginProtocolType},
471            secure_channel::{EstablishedSecureChannel, test::MockedRendezvousServer},
472        },
473        http_client::HttpClient,
474        test_utils::mocks::MatrixMockServer,
475    };
476
477    enum BobBehaviour {
478        HappyPath,
479        UnexpectedMessageInsteadOfLoginProtocol,
480        LoginFailureInsteadOfLoginProtocol,
481        UnexpectedMessageInsteadOfLoginSuccess,
482        LoginFailureInsteadOfLoginSuccess,
483        DeviceAlreadyExists,
484        DeviceNotCreated,
485        InvalidJsonMessage,
486        CancelledWhileWaitingForAuth,
487        UnsupportedProtocol,
488    }
489
490    #[allow(clippy::too_many_arguments)]
491    async fn request_login_with_scanned_qr_code(
492        behaviour: BobBehaviour,
493        qr_code_rx: oneshot::Receiver<QrCodeData>,
494        check_code_tx: oneshot::Sender<u8>,
495        server: Option<MatrixMockServer>,
496        // The rendezvous server is here because it contains MockGuards that are
497        // tied to the lifetime of the MatrixMockServer. Otherwise we might
498        // attempt to drop the MatrixMockServer before the MockGuards.
499        _rendezvous_server: &MockedRendezvousServer,
500        device_authorization_grant: Option<AuthorizationGrant>,
501        secrets_bundle: Option<SecretsBundle>,
502    ) {
503        // Wait for Alice to produce the qr code.
504        let qr_code_data = qr_code_rx.await.expect("Bob should receive the QR code");
505
506        // Use the QR code to establish the secure channel from the new client
507        // (Bob).
508        let mut bob = EstablishedSecureChannel::from_qr_code(
509            reqwest::Client::new(),
510            &qr_code_data,
511            QrCodeIntent::Login,
512        )
513        .await
514        .expect("Bob should be able to connect the secure channel");
515
516        // Let Alice know about the checkcode so she can verify the channel.
517        check_code_tx.send(bob.check_code()).expect("Bob should be able to send the checkcode");
518
519        match behaviour {
520            BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol => {
521                // Send an unexpected message and exit.
522                let message = QrAuthMessage::LoginSuccess;
523                bob.send_json(message).await.unwrap();
524                return;
525            }
526            BobBehaviour::LoginFailureInsteadOfLoginProtocol => {
527                // Send a LoginFailure message instead of LoginProtocol.
528                let message = QrAuthMessage::LoginFailure {
529                    reason: LoginFailureReason::UserCancelled,
530                    homeserver: None,
531                };
532                bob.send_json(message).await.unwrap();
533                return;
534            }
535            BobBehaviour::InvalidJsonMessage => {
536                // Send invalid JSON that cannot be deserialized as
537                // QrAuthMessage.
538                bob.send_json(serde_json::json!({"type": "m.login.bogus"})).await.unwrap();
539                return;
540            }
541            BobBehaviour::DeviceAlreadyExists => {
542                // Mock the endpoint for querying devices so that Alice thinks
543                // the device already exists.
544                server
545                    .as_ref()
546                    .expect("Bob needs the server for DeviceAlreadyExists")
547                    .mock_get_device()
548                    .ok()
549                    .expect(1..)
550                    .named("get_device")
551                    .mount()
552                    .await;
553
554                // Now send the LoginProtocol message.
555                let message = QrAuthMessage::LoginProtocol {
556                    protocol: LoginProtocolType::DeviceAuthorizationGrant,
557                    device_authorization_grant: device_authorization_grant
558                        .expect("Bob needs the device authorization grant"),
559                    device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
560                };
561                bob.send_json(message).await.unwrap();
562
563                // Alice should fail the login with the appropriate reason.
564                let message = bob
565                    .receive_json()
566                    .await
567                    .expect("Bob should receive the LoginFailure message from Alice");
568                assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
569                assert_matches!(reason, LoginFailureReason::DeviceAlreadyExists);
570
571                return; // Exit.
572            }
573            BobBehaviour::UnsupportedProtocol => {
574                // Request a protocol that Alice did not offer.
575                let message = QrAuthMessage::LoginProtocol {
576                    protocol: LoginProtocolType::from("m.unknown_protocol"),
577                    device_authorization_grant: device_authorization_grant
578                        .expect("Bob needs the device authorization grant"),
579                    device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
580                };
581                bob.send_json(message).await.unwrap();
582
583                // Alice should reject the protocol.
584                let message = bob
585                    .receive_json()
586                    .await
587                    .expect("Bob should receive the LoginFailure message from Alice");
588                assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
589                assert_matches!(reason, LoginFailureReason::UnsupportedProtocol);
590
591                return; // Exit.
592            }
593            BobBehaviour::CancelledWhileWaitingForAuth => {
594                // Send the LoginProtocol message.
595                let message = QrAuthMessage::LoginProtocol {
596                    protocol: LoginProtocolType::DeviceAuthorizationGrant,
597                    device_authorization_grant: device_authorization_grant
598                        .expect("Bob needs the device authorization grant"),
599                    device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
600                };
601                bob.send_json(message).await.unwrap();
602
603                // Alice cancels while waiting for the authorization and should
604                // fail the login with the appropriate reason.
605                let message = bob
606                    .receive_json()
607                    .await
608                    .expect("Bob should receive the LoginFailure message from Alice");
609                assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
610                assert_matches!(reason, LoginFailureReason::UserCancelled);
611
612                return; // Exit.
613            }
614            _ => {
615                // Send the LoginProtocol message.
616                let message = QrAuthMessage::LoginProtocol {
617                    protocol: LoginProtocolType::DeviceAuthorizationGrant,
618                    device_authorization_grant: device_authorization_grant
619                        .expect("Bob needs the device authorization grant"),
620                    device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
621                };
622                bob.send_json(message).await.unwrap();
623            }
624        }
625
626        // Receive the LoginProtocolAccepted message.
627        let message = bob
628            .receive_json()
629            .await
630            .expect("Bob should receive the LoginProtocolAccepted message from Alice");
631        assert_let!(QrAuthMessage::LoginProtocolAccepted = message);
632
633        match behaviour {
634            BobBehaviour::UnexpectedMessageInsteadOfLoginSuccess => {
635                // Send an unexpected message instead of LoginSuccess.
636                let message = QrAuthMessage::LoginProtocolAccepted;
637                bob.send_json(message).await.unwrap();
638                return;
639            }
640            BobBehaviour::LoginFailureInsteadOfLoginSuccess => {
641                // Send a LoginFailure message instead of LoginSuccess.
642                let message = QrAuthMessage::LoginFailure {
643                    reason: LoginFailureReason::AuthorizationExpired,
644                    homeserver: None,
645                };
646                bob.send_json(message).await.unwrap();
647                return;
648            }
649            BobBehaviour::DeviceNotCreated => {
650                // Don't mock the endpoint for querying devices so that Alice
651                // cannot verify that we have logged in.
652
653                // Send the LoginSuccess message to claim that we have logged
654                // in.
655                let message = QrAuthMessage::LoginSuccess;
656                bob.send_json(message).await.unwrap();
657
658                // Alice should eventually give up querying our device and fail
659                // the login with the appropriate reason.
660                let message = bob
661                    .receive_json()
662                    .await
663                    .expect("Bob should receive the LoginFailure message from Alice");
664                assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
665                assert_matches!(reason, LoginFailureReason::DeviceNotFound);
666
667                return; // Exit.
668            }
669            _ => {
670                // Mock the endpoint for querying devices so that Alice thinks
671                // we have logged in.
672                server
673                    .as_ref()
674                    .expect("Bob needs the server for HappyPath")
675                    .mock_get_device()
676                    .ok()
677                    .expect(1..)
678                    .named("get_device")
679                    .mount()
680                    .await;
681
682                // Send the LoginSuccess message.
683                let message = QrAuthMessage::LoginSuccess;
684                bob.send_json(message).await.unwrap();
685            }
686        }
687
688        // Receive the LoginSecrets message.
689        let message = bob
690            .receive_json()
691            .await
692            .expect("Bob should receive the LoginSecrets message from Alice");
693        assert_let!(QrAuthMessage::LoginSecrets(bundle) = message);
694
695        // Verify that we received the correct secrets.
696        assert_eq!(
697            serde_json::to_value(&secrets_bundle).unwrap(),
698            serde_json::to_value(&bundle).unwrap()
699        );
700    }
701
702    #[allow(clippy::too_many_arguments)]
703    async fn request_login_with_generated_qr_code(
704        behaviour: BobBehaviour,
705        channel: SecureChannel,
706        check_code_rx: oneshot::Receiver<u8>,
707        server: Option<MatrixMockServer>,
708        // The rendezvous server is here because it contains MockGuards that are
709        // tied to the lifetime of the MatrixMockServer. Otherwise we might
710        // attempt to drop the MatrixMockServer before the MockGuards.
711        _rendezvous_server: &MockedRendezvousServer,
712        homeserver: Url,
713        device_authorization_grant: Option<AuthorizationGrant>,
714        secrets_bundle: Option<SecretsBundle>,
715    ) {
716        // Wait for Alice to scan the qr code and connect the secure channel.
717        let channel =
718            channel.connect().await.expect("Bob should be able to connect the secure channel");
719
720        // Wait for Alice to send us the checkcode and use it to verify the
721        // channel.
722        let check_code = check_code_rx.await.expect("Bob should receive the checkcode");
723        let mut bob = channel
724            .confirm(check_code)
725            .expect("Bob should be able to confirm the channel is secure");
726
727        // Receive the LoginProtocols message.
728        let message = bob
729            .receive_json()
730            .await
731            .expect("Bob should receive the LoginProtocolAccepted message from Alice");
732        assert_let!(
733            QrAuthMessage::LoginProtocols { protocols, homeserver: alice_homeserver } = message
734        );
735        assert_eq!(protocols, vec![LoginProtocolType::DeviceAuthorizationGrant]);
736        assert_eq!(alice_homeserver, homeserver);
737
738        match behaviour {
739            BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol => {
740                // Send an unexpected message and exit.
741                let message = QrAuthMessage::LoginSuccess;
742                bob.send_json(message).await.unwrap();
743                return;
744            }
745            BobBehaviour::LoginFailureInsteadOfLoginProtocol => {
746                // Send a LoginFailure message instead of LoginProtocol.
747                let message = QrAuthMessage::LoginFailure {
748                    reason: LoginFailureReason::UserCancelled,
749                    homeserver: None,
750                };
751                bob.send_json(message).await.unwrap();
752                return;
753            }
754            BobBehaviour::InvalidJsonMessage => {
755                // Send invalid JSON that cannot be deserialized as
756                // QrAuthMessage.
757                bob.send_json(serde_json::json!({"type": "m.login.bogus"})).await.unwrap();
758                return;
759            }
760            BobBehaviour::DeviceAlreadyExists => {
761                // Mock the endpoint for querying devices so that Alice thinks
762                // the device already exists.
763                server
764                    .as_ref()
765                    .expect("Bob needs the MatrixMockServer")
766                    .mock_get_device()
767                    .ok()
768                    .expect(1..)
769                    .named("get_device")
770                    .mount()
771                    .await;
772
773                // Now send the LoginProtocol message.
774                let message = QrAuthMessage::LoginProtocol {
775                    protocol: LoginProtocolType::DeviceAuthorizationGrant,
776                    device_authorization_grant: device_authorization_grant
777                        .expect("Bob needs the device authorization grant"),
778                    device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
779                };
780                bob.send_json(message).await.unwrap();
781
782                // Alice should fail the login with the appropriate reason.
783                let message = bob
784                    .receive_json()
785                    .await
786                    .expect("Bob should receive the LoginFailure message from Alice");
787                assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
788                assert_matches!(reason, LoginFailureReason::DeviceAlreadyExists);
789
790                return; // Exit.
791            }
792            BobBehaviour::CancelledWhileWaitingForAuth => {
793                // Send the LoginProtocol message.
794                let message = QrAuthMessage::LoginProtocol {
795                    protocol: LoginProtocolType::DeviceAuthorizationGrant,
796                    device_authorization_grant: device_authorization_grant
797                        .expect("Bob needs the device authorization grant"),
798                    device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
799                };
800                bob.send_json(message).await.unwrap();
801
802                // Alice cancels while waiting for the authorization and should
803                // fail the login with the appropriate reason.
804                let message = bob
805                    .receive_json()
806                    .await
807                    .expect("Bob should receive the LoginFailure message from Alice");
808                assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
809                assert_matches!(reason, LoginFailureReason::UserCancelled);
810
811                return; // Exit.
812            }
813            _ => {
814                // Send the LoginProtocol message.
815                let message = QrAuthMessage::LoginProtocol {
816                    protocol: LoginProtocolType::DeviceAuthorizationGrant,
817                    device_authorization_grant: device_authorization_grant
818                        .expect("Bob needs the device authorization grant"),
819                    device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
820                };
821                bob.send_json(message).await.unwrap();
822            }
823        }
824
825        // Receive the LoginProtocolAccepted message.
826        let message = bob
827            .receive_json()
828            .await
829            .expect("Bob should receive the LoginProtocolAccepted message from Alice");
830        assert_let!(QrAuthMessage::LoginProtocolAccepted = message);
831
832        match behaviour {
833            BobBehaviour::UnexpectedMessageInsteadOfLoginSuccess => {
834                // Send an unexpected message instead of LoginSuccess.
835                let message = QrAuthMessage::LoginProtocolAccepted;
836                bob.send_json(message).await.unwrap();
837                return;
838            }
839            BobBehaviour::LoginFailureInsteadOfLoginSuccess => {
840                // Send a LoginFailure message instead of LoginSuccess.
841                let message = QrAuthMessage::LoginFailure {
842                    reason: LoginFailureReason::AuthorizationExpired,
843                    homeserver: None,
844                };
845                bob.send_json(message).await.unwrap();
846                return;
847            }
848            BobBehaviour::DeviceNotCreated => {
849                // Don't mock the endpoint for querying devices so that Alice
850                // cannot verify that we have logged in.
851
852                // Send the LoginSuccess message to claim that we have logged
853                // in.
854                let message = QrAuthMessage::LoginSuccess;
855                bob.send_json(message).await.unwrap();
856
857                // Alice should eventually give up querying our device and fail
858                // the login with the appropriate reason.
859                let message = bob
860                    .receive_json()
861                    .await
862                    .expect("Bob should receive the LoginFailure message from Alice");
863                assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
864                assert_matches!(reason, LoginFailureReason::DeviceNotFound);
865
866                return; // Exit.
867            }
868            _ => {
869                // Mock the endpoint for querying devices so that Alice thinks
870                // we have logged in.
871                server
872                    .as_ref()
873                    .expect("Bob needs the MatrixMockServer")
874                    .mock_get_device()
875                    .ok()
876                    .expect(1..)
877                    .named("get_device")
878                    .mount()
879                    .await;
880
881                // Send the LoginSuccess message.
882                let message = QrAuthMessage::LoginSuccess;
883                bob.send_json(message).await.unwrap();
884            }
885        }
886
887        // Receive the LoginSecrets message.
888        let message = bob
889            .receive_json()
890            .await
891            .expect("Bob should receive the LoginSecrets message from Alice");
892        assert_let!(QrAuthMessage::LoginSecrets(bundle) = message);
893
894        // Verify that we received the correct secrets.
895        assert_eq!(
896            serde_json::to_value(&secrets_bundle).unwrap(),
897            serde_json::to_value(&bundle).unwrap()
898        );
899    }
900
901    #[async_test]
902    async fn test_grant_login_with_generated_qr_code() {
903        let server = MatrixMockServer::new().await;
904        let rendezvous_server =
905            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
906        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
907
908        let device_authorization_grant = AuthorizationGrant {
909            verification_uri_complete: Some(VerificationUriComplete::new(
910                "https://id.matrix.org/device/abcde".to_owned(),
911            )),
912            verification_uri: EndUserVerificationUrl::new(
913                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
914            )
915            .unwrap(),
916        };
917
918        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
919        server
920            .mock_upload_cross_signing_keys()
921            .ok()
922            .expect(1)
923            .named("upload_xsigning_keys")
924            .mount()
925            .await;
926        server
927            .mock_upload_cross_signing_signatures()
928            .ok()
929            .expect(1)
930            .named("upload_xsigning_signatures")
931            .mount()
932            .await;
933
934        // Create the existing client (Alice).
935        let user_id = owned_user_id!("@alice:example.org");
936        let device_id = owned_device_id!("ALICE_DEVICE");
937        let alice = server
938            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
939            .logged_in_with_oauth()
940            .build()
941            .await;
942        alice
943            .encryption()
944            .bootstrap_cross_signing(None)
945            .await
946            .expect("Alice should be able to set up cross signing");
947
948        // Prepare the login granting future.
949        let oauth = alice.oauth();
950        let grant = oauth
951            .grant_login_with_qr_code()
952            .device_creation_timeout(Duration::from_secs(2))
953            .generate();
954        let secrets_bundle = export_secrets_bundle(&alice)
955            .await
956            .expect("Alice should be able to export the secrets bundle");
957        let (qr_code_tx, qr_code_rx) = oneshot::channel();
958        let (checkcode_tx, checkcode_rx) = oneshot::channel();
959
960        // Spawn the updates task.
961        let mut updates = grant.subscribe_to_progress();
962        let mut state = grant.state.get();
963        let verification_uri_complete =
964            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
965        assert_matches!(state.clone(), GrantLoginProgress::Starting);
966        let updates_task = spawn(async move {
967            let mut qr_code_tx = Some(qr_code_tx);
968            let mut checkcode_rx = Some(checkcode_rx);
969
970            while let Some(update) = updates.next().await {
971                match &update {
972                    GrantLoginProgress::Starting => {
973                        assert_matches!(state, GrantLoginProgress::Starting);
974                    }
975                    GrantLoginProgress::EstablishingSecureChannel(
976                        GeneratedQrProgress::QrReady(qr_code_data),
977                    ) => {
978                        assert_matches!(state, GrantLoginProgress::Starting);
979                        qr_code_tx
980                            .take()
981                            .expect("The QR code should only be forwarded once")
982                            .send(qr_code_data.clone())
983                            .expect("Alice should be able to forward the QR code");
984                    }
985                    GrantLoginProgress::EstablishingSecureChannel(
986                        GeneratedQrProgress::QrScanned(checkcode_sender),
987                    ) => {
988                        assert_matches!(
989                            state,
990                            GrantLoginProgress::EstablishingSecureChannel(
991                                GeneratedQrProgress::QrReady(_)
992                            )
993                        );
994                        let checkcode = checkcode_rx
995                            .take()
996                            .expect("The checkcode should only be forwarded once")
997                            .await
998                            .expect("Alice should receive the checkcode");
999                        checkcode_sender
1000                            .send(checkcode)
1001                            .await
1002                            .expect("Alice should be able to forward the checkcode");
1003                    }
1004                    GrantLoginProgress::WaitingForAuth {
1005                        verification_uri,
1006                        continuation_sender,
1007                    } => {
1008                        assert_matches!(
1009                            state,
1010                            GrantLoginProgress::EstablishingSecureChannel(
1011                                GeneratedQrProgress::QrScanned(_)
1012                            )
1013                        );
1014                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
1015                        continuation_sender.confirm().await.expect("should be able to confirm");
1016                    }
1017                    GrantLoginProgress::SyncingSecrets => {
1018                        assert_matches!(state, GrantLoginProgress::WaitingForAuth { .. });
1019                    }
1020                    GrantLoginProgress::Done => {
1021                        assert_matches!(state, GrantLoginProgress::SyncingSecrets);
1022                        break;
1023                    }
1024                }
1025                state = update;
1026            }
1027        });
1028
1029        // Let Bob request the login and run through the process.
1030        let bob_task = spawn(async move {
1031            request_login_with_scanned_qr_code(
1032                BobBehaviour::HappyPath,
1033                qr_code_rx,
1034                checkcode_tx,
1035                Some(server),
1036                &rendezvous_server,
1037                Some(device_authorization_grant),
1038                Some(secrets_bundle),
1039            )
1040            .await;
1041        });
1042
1043        // Wait for all tasks to finish.
1044        grant.await.expect("Alice should be able to grant the login");
1045        updates_task.await.expect("Alice should run through all progress states");
1046        bob_task.await.expect("Bob's task should finish");
1047    }
1048
1049    #[async_test]
1050    async fn test_grant_login_with_scanned_qr_code() {
1051        let server = MatrixMockServer::new().await;
1052        let rendezvous_server =
1053            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1054        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1055
1056        let device_authorization_grant = AuthorizationGrant {
1057            verification_uri_complete: Some(VerificationUriComplete::new(
1058                "https://id.matrix.org/device/abcde".to_owned(),
1059            )),
1060            verification_uri: EndUserVerificationUrl::new(
1061                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1062            )
1063            .unwrap(),
1064        };
1065
1066        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1067        server
1068            .mock_upload_cross_signing_keys()
1069            .ok()
1070            .expect(1)
1071            .named("upload_xsigning_keys")
1072            .mount()
1073            .await;
1074        server
1075            .mock_upload_cross_signing_signatures()
1076            .ok()
1077            .expect(1)
1078            .named("upload_xsigning_signatures")
1079            .mount()
1080            .await;
1081
1082        // Create a secure channel on the new client (Bob) and extract the QR
1083        // code.
1084        let client = HttpClient::new(reqwest::Client::new(), Default::default());
1085        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1086            .await
1087            .expect("Bob should be able to create a secure channel.");
1088        let qr_code_data = channel.qr_code_data().clone();
1089
1090        // Create the existing client (Alice).
1091        let user_id = owned_user_id!("@alice:example.org");
1092        let device_id = owned_device_id!("ALICE_DEVICE");
1093        let alice = server
1094            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1095            .logged_in_with_oauth()
1096            .build()
1097            .await;
1098        alice
1099            .encryption()
1100            .bootstrap_cross_signing(None)
1101            .await
1102            .expect("Alice should be able to set up cross signing");
1103
1104        // Prepare the login granting future using the QR code.
1105        let oauth = alice.oauth();
1106        let grant = oauth
1107            .grant_login_with_qr_code()
1108            .device_creation_timeout(Duration::from_secs(2))
1109            .scan(&qr_code_data);
1110        let secrets_bundle = export_secrets_bundle(&alice)
1111            .await
1112            .expect("Alice should be able to export the secrets bundle");
1113        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1114
1115        // Spawn the updates task.
1116        let mut updates = grant.subscribe_to_progress();
1117        let mut state = grant.state.get();
1118        let verification_uri_complete =
1119            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
1120        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1121        let updates_task = spawn(async move {
1122            let mut checkcode_tx = Some(checkcode_tx);
1123
1124            while let Some(update) = updates.next().await {
1125                match &update {
1126                    GrantLoginProgress::Starting => {
1127                        assert_matches!(state, GrantLoginProgress::Starting);
1128                    }
1129                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1130                        assert_matches!(state, GrantLoginProgress::Starting);
1131                        checkcode_tx
1132                            .take()
1133                            .expect("The checkcode should only be forwarded once")
1134                            .send(*check_code)
1135                            .expect("Alice should be able to forward the checkcode");
1136                    }
1137                    GrantLoginProgress::WaitingForAuth {
1138                        verification_uri,
1139                        continuation_sender,
1140                    } => {
1141                        assert_matches!(
1142                            state,
1143                            GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
1144                        );
1145                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
1146                        continuation_sender.confirm().await.expect("should be able to confirm");
1147                    }
1148                    GrantLoginProgress::SyncingSecrets => {
1149                        assert_matches!(state, GrantLoginProgress::WaitingForAuth { .. });
1150                    }
1151                    GrantLoginProgress::Done => {
1152                        assert_matches!(state, GrantLoginProgress::SyncingSecrets);
1153                        break;
1154                    }
1155                }
1156                state = update;
1157            }
1158        });
1159
1160        // Let Bob request the login and run through the process.
1161        let bob_task = spawn(async move {
1162            request_login_with_generated_qr_code(
1163                BobBehaviour::HappyPath,
1164                channel,
1165                checkcode_rx,
1166                Some(server),
1167                &rendezvous_server,
1168                alice.homeserver(),
1169                Some(device_authorization_grant),
1170                Some(secrets_bundle),
1171            )
1172            .await;
1173        });
1174
1175        // Wait for all tasks to finish.
1176        grant.await.expect("Alice should be able to grant the login");
1177        updates_task.await.expect("Alice should run through all progress states");
1178        bob_task.await.expect("Bob's task should finish");
1179    }
1180
1181    #[async_test]
1182    async fn test_grant_login_with_scanned_qr_code_with_homeserver_swap() {
1183        let server = MatrixMockServer::new().await;
1184        let rendezvous_server =
1185            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1186        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1187
1188        let device_authorization_grant = AuthorizationGrant {
1189            verification_uri_complete: Some(VerificationUriComplete::new(
1190                "https://id.matrix.org/device/abcde".to_owned(),
1191            )),
1192            verification_uri: EndUserVerificationUrl::new(
1193                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1194            )
1195            .unwrap(),
1196        };
1197
1198        let login_server = MatrixMockServer::new().await;
1199
1200        login_server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1201        login_server
1202            .mock_upload_cross_signing_keys()
1203            .ok()
1204            .expect(1)
1205            .named("upload_xsigning_keys")
1206            .mount()
1207            .await;
1208        login_server
1209            .mock_upload_cross_signing_signatures()
1210            .ok()
1211            .expect(1)
1212            .named("upload_xsigning_signatures")
1213            .mount()
1214            .await;
1215
1216        // Create a secure channel on the new client (Bob) and extract the QR
1217        // code.
1218        let client = HttpClient::new(reqwest::Client::new(), Default::default());
1219        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1220            .await
1221            .expect("Bob should be able to create a secure channel.");
1222        let qr_code_data = channel.qr_code_data().clone();
1223
1224        // Create the existing client (Alice).
1225        let user_id = owned_user_id!("@alice:example.org");
1226        let device_id = owned_device_id!("ALICE_DEVICE");
1227        let alice = login_server
1228            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1229            .logged_in_with_oauth()
1230            .build()
1231            .await;
1232        alice
1233            .encryption()
1234            .bootstrap_cross_signing(None)
1235            .await
1236            .expect("Alice should be able to set up cross signing");
1237
1238        // Prepare the login granting future using the QR code.
1239        let oauth = alice.oauth();
1240        let grant = oauth
1241            .grant_login_with_qr_code()
1242            .device_creation_timeout(Duration::from_secs(2))
1243            .scan(&qr_code_data);
1244        let secrets_bundle = export_secrets_bundle(&alice)
1245            .await
1246            .expect("Alice should be able to export the secrets bundle");
1247        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1248
1249        // Spawn the updates task.
1250        let mut updates = grant.subscribe_to_progress();
1251        let mut state = grant.state.get();
1252        let verification_uri_complete =
1253            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
1254        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1255        let updates_task = spawn(async move {
1256            let mut checkcode_tx = Some(checkcode_tx);
1257
1258            while let Some(update) = updates.next().await {
1259                match &update {
1260                    GrantLoginProgress::Starting => {
1261                        assert_matches!(state, GrantLoginProgress::Starting);
1262                    }
1263                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1264                        assert_matches!(state, GrantLoginProgress::Starting);
1265                        checkcode_tx
1266                            .take()
1267                            .expect("The checkcode should only be forwarded once")
1268                            .send(*check_code)
1269                            .expect("Alice should be able to forward the checkcode");
1270                    }
1271                    GrantLoginProgress::WaitingForAuth {
1272                        verification_uri,
1273                        continuation_sender,
1274                    } => {
1275                        assert_matches!(
1276                            state,
1277                            GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
1278                        );
1279                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
1280                        continuation_sender.confirm().await.expect("should be able to confirm");
1281                    }
1282                    GrantLoginProgress::SyncingSecrets => {
1283                        assert_matches!(state, GrantLoginProgress::WaitingForAuth { .. });
1284                    }
1285                    GrantLoginProgress::Done => {
1286                        assert_matches!(state, GrantLoginProgress::SyncingSecrets);
1287                        break;
1288                    }
1289                }
1290                state = update;
1291            }
1292        });
1293
1294        // Let Bob request the login and run through the process.
1295        let bob_task = spawn(async move {
1296            request_login_with_generated_qr_code(
1297                BobBehaviour::HappyPath,
1298                channel,
1299                checkcode_rx,
1300                Some(login_server),
1301                &rendezvous_server,
1302                alice.homeserver(),
1303                Some(device_authorization_grant),
1304                Some(secrets_bundle),
1305            )
1306            .await;
1307        });
1308
1309        // Wait for all tasks to finish.
1310        grant.await.expect("Alice should be able to grant the login");
1311        updates_task.await.expect("Alice should run through all progress states");
1312        bob_task.await.expect("Bob's task should finish");
1313    }
1314
1315    #[async_test]
1316    async fn test_grant_login_with_generated_qr_code_unexpected_message_instead_of_login_protocol()
1317    {
1318        let server = MatrixMockServer::new().await;
1319        let rendezvous_server = Arc::new(
1320            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await,
1321        );
1322        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1323
1324        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1325        server
1326            .mock_upload_cross_signing_keys()
1327            .ok()
1328            .expect(1)
1329            .named("upload_xsigning_keys")
1330            .mount()
1331            .await;
1332        server
1333            .mock_upload_cross_signing_signatures()
1334            .ok()
1335            .expect(1)
1336            .named("upload_xsigning_signatures")
1337            .mount()
1338            .await;
1339
1340        // Create the existing client (Alice).
1341        let user_id = owned_user_id!("@alice:example.org");
1342        let device_id = owned_device_id!("ALICE_DEVICE");
1343        let alice = server
1344            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1345            .logged_in_with_oauth()
1346            .build()
1347            .await;
1348        alice
1349            .encryption()
1350            .bootstrap_cross_signing(None)
1351            .await
1352            .expect("Alice should be able to set up cross signing");
1353
1354        // Prepare the login granting future.
1355        let oauth = alice.oauth();
1356        let grant = oauth
1357            .grant_login_with_qr_code()
1358            .device_creation_timeout(Duration::from_secs(2))
1359            .generate();
1360        let (qr_code_tx, qr_code_rx) = oneshot::channel();
1361        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1362
1363        // Spawn the updates task.
1364        let mut updates = grant.subscribe_to_progress();
1365        let mut state = grant.state.get();
1366        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1367        let updates_task = spawn(async move {
1368            let mut qr_code_tx = Some(qr_code_tx);
1369            let mut checkcode_rx = Some(checkcode_rx);
1370
1371            while let Some(update) = updates.next().await {
1372                match &update {
1373                    GrantLoginProgress::Starting => {
1374                        assert_matches!(state, GrantLoginProgress::Starting);
1375                    }
1376                    GrantLoginProgress::EstablishingSecureChannel(
1377                        GeneratedQrProgress::QrReady(qr_code_data),
1378                    ) => {
1379                        assert_matches!(state, GrantLoginProgress::Starting);
1380                        qr_code_tx
1381                            .take()
1382                            .expect("The QR code should only be forwarded once")
1383                            .send(qr_code_data.clone())
1384                            .expect("Alice should be able to forward the QR code");
1385                    }
1386                    GrantLoginProgress::EstablishingSecureChannel(
1387                        GeneratedQrProgress::QrScanned(checkcode_sender),
1388                    ) => {
1389                        assert_matches!(
1390                            state,
1391                            GrantLoginProgress::EstablishingSecureChannel(
1392                                GeneratedQrProgress::QrReady(_)
1393                            )
1394                        );
1395                        let checkcode = checkcode_rx
1396                            .take()
1397                            .expect("The checkcode should only be forwarded once")
1398                            .await
1399                            .expect("Alice should receive the checkcode");
1400                        checkcode_sender
1401                            .send(checkcode)
1402                            .await
1403                            .expect("Alice should be able to forward the checkcode");
1404                        break;
1405                    }
1406                    _ => {
1407                        panic!("Alice should abort the process");
1408                    }
1409                }
1410                state = update;
1411            }
1412        });
1413
1414        // Let Bob request the login and run through the process.
1415        let rendezvous_server_clone = rendezvous_server.clone();
1416        let bob_task = spawn(async move {
1417            request_login_with_scanned_qr_code(
1418                BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol,
1419                qr_code_rx,
1420                checkcode_tx,
1421                None,
1422                &rendezvous_server_clone,
1423                None,
1424                None,
1425            )
1426            .await;
1427        });
1428
1429        // Wait for all tasks to finish / fail.
1430        assert_let!(
1431            Err(QRCodeGrantLoginError::UnexpectedMessage {
1432                expected: "m.login.protocol",
1433                received,
1434            }) = grant.await,
1435            "Alice should abort the login with expected error variant"
1436        );
1437        assert_matches!(
1438            *received,
1439            QrAuthMessage::LoginSuccess,
1440            "Alice should abort the login with expected error message"
1441        );
1442        updates_task.await.expect("Alice should run through all progress states");
1443        bob_task.await.expect("Bob's task should finish");
1444    }
1445
1446    #[async_test]
1447    async fn test_grant_login_with_scanned_qr_code_unexpected_message_instead_of_login_protocol() {
1448        let server = MatrixMockServer::new().await;
1449        let rendezvous_server = Arc::new(
1450            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await,
1451        );
1452        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1453
1454        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1455        server
1456            .mock_upload_cross_signing_keys()
1457            .ok()
1458            .expect(1)
1459            .named("upload_xsigning_keys")
1460            .mount()
1461            .await;
1462        server
1463            .mock_upload_cross_signing_signatures()
1464            .ok()
1465            .expect(1)
1466            .named("upload_xsigning_signatures")
1467            .mount()
1468            .await;
1469
1470        // Create a secure channel on the new client (Bob) and extract the QR
1471        // code.
1472        let client = HttpClient::new(reqwest::Client::new(), Default::default());
1473        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1474            .await
1475            .expect("Bob should be able to create a secure channel.");
1476        let qr_code_data = channel.qr_code_data().clone();
1477
1478        // Create the existing client (Alice).
1479        let user_id = owned_user_id!("@alice:example.org");
1480        let device_id = owned_device_id!("ALICE_DEVICE");
1481        let alice = server
1482            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1483            .logged_in_with_oauth()
1484            .build()
1485            .await;
1486        alice
1487            .encryption()
1488            .bootstrap_cross_signing(None)
1489            .await
1490            .expect("Alice should be able to set up cross signing");
1491
1492        // Prepare the login granting future using the QR code.
1493        let oauth = alice.oauth();
1494        let grant = oauth
1495            .grant_login_with_qr_code()
1496            .device_creation_timeout(Duration::from_secs(2))
1497            .scan(&qr_code_data);
1498        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1499
1500        // Spawn the updates task.
1501        let mut updates = grant.subscribe_to_progress();
1502        let mut state = grant.state.get();
1503        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1504        let updates_task = spawn(async move {
1505            let mut checkcode_tx = Some(checkcode_tx);
1506
1507            while let Some(update) = updates.next().await {
1508                match &update {
1509                    GrantLoginProgress::Starting => {
1510                        assert_matches!(state, GrantLoginProgress::Starting);
1511                    }
1512                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1513                        assert_matches!(state, GrantLoginProgress::Starting);
1514                        checkcode_tx
1515                            .take()
1516                            .expect("The checkcode should only be forwarded once")
1517                            .send(*check_code)
1518                            .expect("Alice should be able to forward the checkcode");
1519                        break;
1520                    }
1521                    _ => {
1522                        panic!("Alice should abort the process");
1523                    }
1524                }
1525                state = update;
1526            }
1527        });
1528
1529        let rendezvous_server_clone = rendezvous_server.clone();
1530        // Let Bob request the login and run through the process.
1531        let bob_task = spawn(async move {
1532            request_login_with_generated_qr_code(
1533                BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol,
1534                channel,
1535                checkcode_rx,
1536                None,
1537                &rendezvous_server_clone,
1538                alice.homeserver(),
1539                None,
1540                None,
1541            )
1542            .await;
1543        });
1544
1545        // Wait for all tasks to finish / fail.
1546        assert_let!(
1547            Err(QRCodeGrantLoginError::UnexpectedMessage {
1548                expected: "m.login.protocol",
1549                received,
1550            }) = grant.await,
1551            "Alice should abort the login with expected error variant"
1552        );
1553        assert_matches!(
1554            *received,
1555            QrAuthMessage::LoginSuccess,
1556            "Alice should abort the login with expected error message"
1557        );
1558        updates_task.await.expect("Alice should run through all progress states");
1559        bob_task.await.expect("Bob's task should finish");
1560    }
1561
1562    #[async_test]
1563    async fn test_grant_login_with_generated_qr_code_unsupported_protocol() {
1564        let server = MatrixMockServer::new().await;
1565        let rendezvous_server =
1566            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1567        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1568
1569        let device_authorization_grant = AuthorizationGrant {
1570            verification_uri_complete: Some(VerificationUriComplete::new(
1571                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1572            )),
1573            verification_uri: EndUserVerificationUrl::new(
1574                "https://id.matrix.org/device/abcde".to_owned(),
1575            )
1576            .unwrap(),
1577        };
1578
1579        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1580        server
1581            .mock_upload_cross_signing_keys()
1582            .ok()
1583            .expect(1)
1584            .named("upload_xsigning_keys")
1585            .mount()
1586            .await;
1587        server
1588            .mock_upload_cross_signing_signatures()
1589            .ok()
1590            .expect(1)
1591            .named("upload_xsigning_signatures")
1592            .mount()
1593            .await;
1594
1595        // Create the existing client (Alice).
1596        let user_id = owned_user_id!("@alice:example.org");
1597        let device_id = owned_device_id!("ALICE_DEVICE");
1598        let alice = server
1599            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1600            .logged_in_with_oauth()
1601            .build()
1602            .await;
1603        alice
1604            .encryption()
1605            .bootstrap_cross_signing(None)
1606            .await
1607            .expect("Alice should be able to set up cross signing");
1608
1609        // Prepare the login granting future.
1610        let oauth = alice.oauth();
1611        let grant = oauth
1612            .grant_login_with_qr_code()
1613            .device_creation_timeout(Duration::from_secs(2))
1614            .generate();
1615
1616        let (qr_code_tx, qr_code_rx) = oneshot::channel();
1617        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1618
1619        // Spawn the updates task.
1620        let mut updates = grant.subscribe_to_progress();
1621        let mut state = grant.state.get();
1622        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1623        let updates_task = spawn(async move {
1624            let mut qr_code_tx = Some(qr_code_tx);
1625            let mut checkcode_rx = Some(checkcode_rx);
1626
1627            while let Some(update) = updates.next().await {
1628                match &update {
1629                    GrantLoginProgress::Starting => {
1630                        assert_matches!(state, GrantLoginProgress::Starting);
1631                    }
1632                    GrantLoginProgress::EstablishingSecureChannel(
1633                        GeneratedQrProgress::QrReady(qr_code_data),
1634                    ) => {
1635                        assert_matches!(state, GrantLoginProgress::Starting);
1636                        qr_code_tx
1637                            .take()
1638                            .expect("The QR code should only be forwarded once")
1639                            .send(qr_code_data.clone())
1640                            .expect("Alice should be able to forward the QR code");
1641                    }
1642                    GrantLoginProgress::EstablishingSecureChannel(
1643                        GeneratedQrProgress::QrScanned(checkcode_sender),
1644                    ) => {
1645                        assert_matches!(
1646                            state,
1647                            GrantLoginProgress::EstablishingSecureChannel(
1648                                GeneratedQrProgress::QrReady(_)
1649                            )
1650                        );
1651                        let checkcode = checkcode_rx
1652                            .take()
1653                            .expect("The checkcode should only be forwarded once")
1654                            .await
1655                            .expect("Alice should receive the checkcode");
1656                        checkcode_sender
1657                            .send(checkcode)
1658                            .await
1659                            .expect("Alice should be able to forward the checkcode");
1660                    }
1661                    _ => {
1662                        panic!("Alice should abort the process");
1663                    }
1664                }
1665                state = update;
1666            }
1667        });
1668
1669        // Let Bob request the login with a protocol Alice did not offer.
1670        let bob_task = spawn(async move {
1671            request_login_with_scanned_qr_code(
1672                BobBehaviour::UnsupportedProtocol,
1673                qr_code_rx,
1674                checkcode_tx,
1675                None,
1676                &rendezvous_server,
1677                Some(device_authorization_grant),
1678                None,
1679            )
1680            .await;
1681        });
1682
1683        // Wait for all tasks to finish / fail.
1684        assert_let!(
1685            Err(QRCodeGrantLoginError::UnsupportedProtocol(protocol)) = grant.await,
1686            "Alice should abort the login with expected error variant"
1687        );
1688        assert_eq!(protocol.as_str(), "m.unknown_protocol");
1689        updates_task.await.expect("Alice should run through all progress states");
1690        bob_task.await.expect("Bob's task should finish");
1691    }
1692
1693    #[async_test]
1694    async fn test_grant_login_with_generated_qr_code_device_already_exists() {
1695        let server = MatrixMockServer::new().await;
1696        let rendezvous_server =
1697            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1698        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1699
1700        let device_authorization_grant = AuthorizationGrant {
1701            verification_uri_complete: Some(VerificationUriComplete::new(
1702                "https://id.matrix.org/device/abcde".to_owned(),
1703            )),
1704            verification_uri: EndUserVerificationUrl::new(
1705                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1706            )
1707            .unwrap(),
1708        };
1709
1710        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1711        server
1712            .mock_upload_cross_signing_keys()
1713            .ok()
1714            .expect(1)
1715            .named("upload_xsigning_keys")
1716            .mount()
1717            .await;
1718        server
1719            .mock_upload_cross_signing_signatures()
1720            .ok()
1721            .expect(1)
1722            .named("upload_xsigning_signatures")
1723            .mount()
1724            .await;
1725
1726        // Create the existing client (Alice).
1727        let user_id = owned_user_id!("@alice:example.org");
1728        let device_id = owned_device_id!("ALICE_DEVICE");
1729        let alice = server
1730            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1731            .logged_in_with_oauth()
1732            .build()
1733            .await;
1734        alice
1735            .encryption()
1736            .bootstrap_cross_signing(None)
1737            .await
1738            .expect("Alice should be able to set up cross signing");
1739
1740        // Prepare the login granting future.
1741        let oauth = alice.oauth();
1742        let grant = oauth
1743            .grant_login_with_qr_code()
1744            .device_creation_timeout(Duration::from_secs(2))
1745            .generate();
1746        let (qr_code_tx, qr_code_rx) = oneshot::channel();
1747        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1748
1749        // Spawn the updates task.
1750        let mut updates = grant.subscribe_to_progress();
1751        let mut state = grant.state.get();
1752        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1753        let updates_task = spawn(async move {
1754            let mut qr_code_tx = Some(qr_code_tx);
1755            let mut checkcode_rx = Some(checkcode_rx);
1756
1757            while let Some(update) = updates.next().await {
1758                match &update {
1759                    GrantLoginProgress::Starting => {
1760                        assert_matches!(state, GrantLoginProgress::Starting);
1761                    }
1762                    GrantLoginProgress::EstablishingSecureChannel(
1763                        GeneratedQrProgress::QrReady(qr_code_data),
1764                    ) => {
1765                        assert_matches!(state, GrantLoginProgress::Starting);
1766                        qr_code_tx
1767                            .take()
1768                            .expect("The QR code should only be forwarded once")
1769                            .send(qr_code_data.clone())
1770                            .expect("Alice should be able to forward the QR code");
1771                    }
1772                    GrantLoginProgress::EstablishingSecureChannel(
1773                        GeneratedQrProgress::QrScanned(checkcode_sender),
1774                    ) => {
1775                        assert_matches!(
1776                            state,
1777                            GrantLoginProgress::EstablishingSecureChannel(
1778                                GeneratedQrProgress::QrReady(_)
1779                            )
1780                        );
1781                        let checkcode = checkcode_rx
1782                            .take()
1783                            .expect("The checkcode should only be forwarded once")
1784                            .await
1785                            .expect("Alice should receive the checkcode");
1786                        checkcode_sender
1787                            .send(checkcode)
1788                            .await
1789                            .expect("Alice should be able to forward the checkcode");
1790                    }
1791                    _ => {
1792                        panic!("Alice should abort the process");
1793                    }
1794                }
1795                state = update;
1796            }
1797        });
1798
1799        // Let Bob request the login and run through the process.
1800        let bob_task = spawn(async move {
1801            request_login_with_scanned_qr_code(
1802                BobBehaviour::DeviceAlreadyExists,
1803                qr_code_rx,
1804                checkcode_tx,
1805                Some(server),
1806                &rendezvous_server,
1807                Some(device_authorization_grant),
1808                None,
1809            )
1810            .await;
1811        });
1812
1813        // Wait for all tasks to finish.
1814        assert_matches!(
1815            grant.await,
1816            Err(QRCodeGrantLoginError::DeviceIDAlreadyInUse),
1817            "Alice should abort the login with expected error"
1818        );
1819        updates_task.await.expect("Alice should run through all progress states");
1820        bob_task.await.expect("Bob's task should finish");
1821    }
1822
1823    #[async_test]
1824    async fn test_grant_login_with_scanned_qr_code_device_already_exists() {
1825        let server = MatrixMockServer::new().await;
1826        let rendezvous_server =
1827            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1828        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1829
1830        let device_authorization_grant = AuthorizationGrant {
1831            verification_uri_complete: Some(VerificationUriComplete::new(
1832                "https://id.matrix.org/device/abcde".to_owned(),
1833            )),
1834            verification_uri: EndUserVerificationUrl::new(
1835                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1836            )
1837            .unwrap(),
1838        };
1839
1840        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1841        server
1842            .mock_upload_cross_signing_keys()
1843            .ok()
1844            .expect(1)
1845            .named("upload_xsigning_keys")
1846            .mount()
1847            .await;
1848        server
1849            .mock_upload_cross_signing_signatures()
1850            .ok()
1851            .expect(1)
1852            .named("upload_xsigning_signatures")
1853            .mount()
1854            .await;
1855
1856        // Create a secure channel on the new client (Bob) and extract the QR
1857        // code.
1858        let client = HttpClient::new(reqwest::Client::new(), Default::default());
1859        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1860            .await
1861            .expect("Bob should be able to create a secure channel.");
1862        let qr_code_data = channel.qr_code_data().clone();
1863
1864        // Create the existing client (Alice).
1865        let user_id = owned_user_id!("@alice:example.org");
1866        let device_id = owned_device_id!("ALICE_DEVICE");
1867        let alice = server
1868            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1869            .logged_in_with_oauth()
1870            .build()
1871            .await;
1872        alice
1873            .encryption()
1874            .bootstrap_cross_signing(None)
1875            .await
1876            .expect("Alice should be able to set up cross signing");
1877
1878        // Prepare the login granting future using the QR code.
1879        let oauth = alice.oauth();
1880        let grant = oauth
1881            .grant_login_with_qr_code()
1882            .device_creation_timeout(Duration::from_secs(2))
1883            .scan(&qr_code_data);
1884        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1885
1886        // Spawn the updates task.
1887        let mut updates = grant.subscribe_to_progress();
1888        let mut state = grant.state.get();
1889        assert_matches!(state.clone(), GrantLoginProgress::Starting);
1890        let updates_task = spawn(async move {
1891            let mut checkcode_tx = Some(checkcode_tx);
1892
1893            while let Some(update) = updates.next().await {
1894                match &update {
1895                    GrantLoginProgress::Starting => {
1896                        assert_matches!(state, GrantLoginProgress::Starting);
1897                    }
1898                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1899                        assert_matches!(state, GrantLoginProgress::Starting);
1900                        checkcode_tx
1901                            .take()
1902                            .expect("The checkcode should only be forwarded once")
1903                            .send(*check_code)
1904                            .expect("Alice should be able to forward the checkcode");
1905                    }
1906                    _ => {
1907                        panic!("Alice should abort the process");
1908                    }
1909                }
1910                state = update;
1911            }
1912        });
1913
1914        // Let Bob request the login and run through the process.
1915        let bob_task = spawn(async move {
1916            request_login_with_generated_qr_code(
1917                BobBehaviour::DeviceAlreadyExists,
1918                channel,
1919                checkcode_rx,
1920                Some(server),
1921                &rendezvous_server,
1922                alice.homeserver(),
1923                Some(device_authorization_grant),
1924                None,
1925            )
1926            .await;
1927        });
1928
1929        // Wait for all tasks to finish.
1930        assert_matches!(
1931            grant.await,
1932            Err(QRCodeGrantLoginError::DeviceIDAlreadyInUse),
1933            "Alice should abort the login with expected error"
1934        );
1935        updates_task.await.expect("Alice should run through all progress states");
1936        bob_task.await.expect("Bob's task should finish");
1937    }
1938
1939    #[async_test]
1940    async fn test_grant_login_with_generated_qr_code_device_not_found() {
1941        let server = MatrixMockServer::new().await;
1942        let rendezvous_server =
1943            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1944        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1945
1946        let device_authorization_grant = AuthorizationGrant {
1947            verification_uri_complete: Some(VerificationUriComplete::new(
1948                "https://id.matrix.org/device/abcde".to_owned(),
1949            )),
1950            verification_uri: EndUserVerificationUrl::new(
1951                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1952            )
1953            .unwrap(),
1954        };
1955
1956        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1957        server
1958            .mock_upload_cross_signing_keys()
1959            .ok()
1960            .expect(1)
1961            .named("upload_xsigning_keys")
1962            .mount()
1963            .await;
1964        server
1965            .mock_upload_cross_signing_signatures()
1966            .ok()
1967            .expect(1)
1968            .named("upload_xsigning_signatures")
1969            .mount()
1970            .await;
1971
1972        // Create the existing client (Alice).
1973        let user_id = owned_user_id!("@alice:example.org");
1974        let device_id = owned_device_id!("ALICE_DEVICE");
1975        let alice = server
1976            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1977            .logged_in_with_oauth()
1978            .build()
1979            .await;
1980        alice
1981            .encryption()
1982            .bootstrap_cross_signing(None)
1983            .await
1984            .expect("Alice should be able to set up cross signing");
1985
1986        // Prepare the login granting future.
1987        let oauth = alice.oauth();
1988        let grant = oauth
1989            .grant_login_with_qr_code()
1990            .device_creation_timeout(Duration::from_secs(2))
1991            .generate();
1992        let (qr_code_tx, qr_code_rx) = oneshot::channel();
1993        let (checkcode_tx, checkcode_rx) = oneshot::channel();
1994
1995        // Spawn the updates task.
1996        let mut updates = grant.subscribe_to_progress();
1997        let mut state = grant.state.get();
1998        let verification_uri_complete =
1999            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
2000        assert_matches!(state.clone(), GrantLoginProgress::Starting);
2001        let updates_task = spawn(async move {
2002            let mut qr_code_tx = Some(qr_code_tx);
2003            let mut checkcode_rx = Some(checkcode_rx);
2004
2005            while let Some(update) = updates.next().await {
2006                match &update {
2007                    GrantLoginProgress::Starting => {
2008                        assert_matches!(state, GrantLoginProgress::Starting);
2009                    }
2010                    GrantLoginProgress::EstablishingSecureChannel(
2011                        GeneratedQrProgress::QrReady(qr_code_data),
2012                    ) => {
2013                        assert_matches!(state, GrantLoginProgress::Starting);
2014                        qr_code_tx
2015                            .take()
2016                            .expect("The QR code should only be forwarded once")
2017                            .send(qr_code_data.clone())
2018                            .expect("Alice should be able to forward the QR code");
2019                    }
2020                    GrantLoginProgress::EstablishingSecureChannel(
2021                        GeneratedQrProgress::QrScanned(checkcode_sender),
2022                    ) => {
2023                        assert_matches!(
2024                            state,
2025                            GrantLoginProgress::EstablishingSecureChannel(
2026                                GeneratedQrProgress::QrReady(_)
2027                            )
2028                        );
2029                        let checkcode = checkcode_rx
2030                            .take()
2031                            .expect("The checkcode should only be forwarded once")
2032                            .await
2033                            .expect("Alice should receive the checkcode");
2034                        checkcode_sender
2035                            .send(checkcode)
2036                            .await
2037                            .expect("Alice should be able to forward the checkcode");
2038                    }
2039                    GrantLoginProgress::WaitingForAuth {
2040                        verification_uri,
2041                        continuation_sender,
2042                    } => {
2043                        assert_matches!(
2044                            state,
2045                            GrantLoginProgress::EstablishingSecureChannel(
2046                                GeneratedQrProgress::QrScanned(_)
2047                            )
2048                        );
2049                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
2050                        continuation_sender.confirm().await.expect("should be able to confirm");
2051                    }
2052                    _ => {
2053                        panic!("Alice should abort the process");
2054                    }
2055                }
2056                state = update;
2057            }
2058        });
2059
2060        // Let Bob request the login and run through the process.
2061        let bob_task = spawn(async move {
2062            request_login_with_scanned_qr_code(
2063                BobBehaviour::DeviceNotCreated,
2064                qr_code_rx,
2065                checkcode_tx,
2066                Some(server),
2067                &rendezvous_server,
2068                Some(device_authorization_grant),
2069                None,
2070            )
2071            .await;
2072        });
2073
2074        assert_matches!(
2075            grant.await,
2076            Err(QRCodeGrantLoginError::DeviceNotFound),
2077            "Alice should abort the login with expected error"
2078        );
2079        updates_task.await.expect("Alice should run through all progress states");
2080        bob_task.await.expect("Bob's task should finish");
2081    }
2082
2083    #[async_test]
2084    async fn test_grant_login_with_scanned_qr_code_device_not_found() {
2085        let server = MatrixMockServer::new().await;
2086        let rendezvous_server =
2087            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
2088        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
2089
2090        let device_authorization_grant = AuthorizationGrant {
2091            verification_uri_complete: Some(VerificationUriComplete::new(
2092                "https://id.matrix.org/device/abcde".to_owned(),
2093            )),
2094            verification_uri: EndUserVerificationUrl::new(
2095                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
2096            )
2097            .unwrap(),
2098        };
2099
2100        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
2101        server
2102            .mock_upload_cross_signing_keys()
2103            .ok()
2104            .expect(1)
2105            .named("upload_xsigning_keys")
2106            .mount()
2107            .await;
2108        server
2109            .mock_upload_cross_signing_signatures()
2110            .ok()
2111            .expect(1)
2112            .named("upload_xsigning_signatures")
2113            .mount()
2114            .await;
2115
2116        // Create a secure channel on the new client (Bob) and extract the QR
2117        // code.
2118        let client = HttpClient::new(reqwest::Client::new(), Default::default());
2119        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
2120            .await
2121            .expect("Bob should be able to create a secure channel.");
2122        let qr_code_data = channel.qr_code_data().clone();
2123
2124        // Create the existing client (Alice).
2125        let user_id = owned_user_id!("@alice:example.org");
2126        let device_id = owned_device_id!("ALICE_DEVICE");
2127        let alice = server
2128            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
2129            .logged_in_with_oauth()
2130            .build()
2131            .await;
2132        alice
2133            .encryption()
2134            .bootstrap_cross_signing(None)
2135            .await
2136            .expect("Alice should be able to set up cross signing");
2137
2138        // Prepare the login granting future using the QR code.
2139        let oauth = alice.oauth();
2140        let grant = oauth
2141            .grant_login_with_qr_code()
2142            .device_creation_timeout(Duration::from_secs(2))
2143            .scan(&qr_code_data);
2144        let (checkcode_tx, checkcode_rx) = oneshot::channel();
2145
2146        // Spawn the updates task.
2147        let mut updates = grant.subscribe_to_progress();
2148        let mut state = grant.state.get();
2149        let verification_uri_complete =
2150            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
2151        assert_matches!(state.clone(), GrantLoginProgress::Starting);
2152        let updates_task = spawn(async move {
2153            let mut checkcode_tx = Some(checkcode_tx);
2154
2155            while let Some(update) = updates.next().await {
2156                match &update {
2157                    GrantLoginProgress::Starting => {
2158                        assert_matches!(state, GrantLoginProgress::Starting);
2159                    }
2160                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
2161                        assert_matches!(state, GrantLoginProgress::Starting);
2162                        checkcode_tx
2163                            .take()
2164                            .expect("The checkcode should only be forwarded once")
2165                            .send(*check_code)
2166                            .expect("Alice should be able to forward the checkcode");
2167                    }
2168                    GrantLoginProgress::WaitingForAuth {
2169                        verification_uri,
2170                        continuation_sender,
2171                    } => {
2172                        assert_matches!(
2173                            state,
2174                            GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
2175                        );
2176                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
2177                        continuation_sender.confirm().await.expect("should be able to confirm");
2178                    }
2179                    _ => {
2180                        panic!("Alice should abort the process");
2181                    }
2182                }
2183                state = update;
2184            }
2185        });
2186
2187        // Let Bob request the login and run through the process.
2188        let bob_task = spawn(async move {
2189            request_login_with_generated_qr_code(
2190                BobBehaviour::DeviceNotCreated,
2191                channel,
2192                checkcode_rx,
2193                None,
2194                &rendezvous_server,
2195                alice.homeserver(),
2196                Some(device_authorization_grant),
2197                None,
2198            )
2199            .await;
2200        });
2201
2202        assert_matches!(
2203            grant.await,
2204            Err(QRCodeGrantLoginError::DeviceNotFound),
2205            "Alice should abort the login with expected error"
2206        );
2207        updates_task.await.expect("Alice should run through all progress states");
2208        bob_task.await.expect("Bob's task should finish");
2209    }
2210
2211    #[async_test]
2212    async fn test_grant_login_with_generated_qr_code_session_expired() {
2213        let server = MatrixMockServer::new().await;
2214        let rendezvous_server =
2215            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::from_secs(2))
2216                .await;
2217        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
2218
2219        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
2220        server
2221            .mock_upload_cross_signing_keys()
2222            .ok()
2223            .expect(1)
2224            .named("upload_xsigning_keys")
2225            .mount()
2226            .await;
2227        server
2228            .mock_upload_cross_signing_signatures()
2229            .ok()
2230            .expect(1)
2231            .named("upload_xsigning_signatures")
2232            .mount()
2233            .await;
2234
2235        // Create the existing client (Alice).
2236        let user_id = owned_user_id!("@alice:example.org");
2237        let device_id = owned_device_id!("ALICE_DEVICE");
2238        let alice = server
2239            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
2240            .logged_in_with_oauth()
2241            .build()
2242            .await;
2243        alice
2244            .encryption()
2245            .bootstrap_cross_signing(None)
2246            .await
2247            .expect("Alice should be able to set up cross signing");
2248
2249        // Prepare the login granting future.
2250        let oauth = alice.oauth();
2251        let grant = oauth
2252            .grant_login_with_qr_code()
2253            .device_creation_timeout(Duration::from_secs(2))
2254            .generate();
2255
2256        // Spawn the updates task.
2257        let mut updates = grant.subscribe_to_progress();
2258        let mut state = grant.state.get();
2259        assert_matches!(state.clone(), GrantLoginProgress::Starting);
2260        let updates_task = spawn(async move {
2261            while let Some(update) = updates.next().await {
2262                match &update {
2263                    GrantLoginProgress::Starting => {
2264                        assert_matches!(state, GrantLoginProgress::Starting);
2265                    }
2266                    GrantLoginProgress::EstablishingSecureChannel(
2267                        GeneratedQrProgress::QrReady(_),
2268                    ) => {
2269                        assert_matches!(state, GrantLoginProgress::Starting);
2270                    }
2271                    _ => {
2272                        panic!("Alice should abort the process");
2273                    }
2274                }
2275                state = update;
2276            }
2277        });
2278
2279        // Bob does not scan the QR code and the channel is never connected.
2280
2281        // Wait for the rendezvous session to time out.
2282        assert_matches!(grant.await, Err(QRCodeGrantLoginError::NotFound));
2283        updates_task.await.expect("Alice should run through all progress states");
2284    }
2285
2286    #[async_test]
2287    async fn test_grant_login_with_scanned_qr_code_session_expired() {
2288        let server = MatrixMockServer::new().await;
2289        let rendezvous_server =
2290            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::from_secs(2))
2291                .await;
2292        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
2293
2294        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
2295        server
2296            .mock_upload_cross_signing_keys()
2297            .ok()
2298            .expect(1)
2299            .named("upload_xsigning_keys")
2300            .mount()
2301            .await;
2302        server
2303            .mock_upload_cross_signing_signatures()
2304            .ok()
2305            .expect(1)
2306            .named("upload_xsigning_signatures")
2307            .mount()
2308            .await;
2309
2310        // Create a secure channel on the new client (Bob) and extract the QR
2311        // code.
2312        let client = HttpClient::new(reqwest::Client::new(), Default::default());
2313        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
2314            .await
2315            .expect("Bob should be able to create a secure channel.");
2316        let qr_code_data = channel.qr_code_data().clone();
2317
2318        // Create the existing client (Alice).
2319        let user_id = owned_user_id!("@alice:example.org");
2320        let device_id = owned_device_id!("ALICE_DEVICE");
2321        let alice = server
2322            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
2323            .logged_in_with_oauth()
2324            .build()
2325            .await;
2326        alice
2327            .encryption()
2328            .bootstrap_cross_signing(None)
2329            .await
2330            .expect("Alice should be able to set up cross signing");
2331
2332        // Prepare the login granting future using the QR code.
2333        let oauth = alice.oauth();
2334        let grant = oauth
2335            .grant_login_with_qr_code()
2336            .device_creation_timeout(Duration::from_secs(2))
2337            .scan(&qr_code_data);
2338
2339        // Spawn the updates task.
2340        let mut updates = grant.subscribe_to_progress();
2341        let mut state = grant.state.get();
2342        assert_matches!(state.clone(), GrantLoginProgress::Starting);
2343        let updates_task = spawn(async move {
2344            while let Some(update) = updates.next().await {
2345                match &update {
2346                    GrantLoginProgress::Starting => {
2347                        assert_matches!(state, GrantLoginProgress::Starting);
2348                    }
2349                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. }) => {
2350                        assert_matches!(state, GrantLoginProgress::Starting);
2351                    }
2352                    _ => {
2353                        panic!("Alice should abort the process");
2354                    }
2355                }
2356                state = update;
2357            }
2358        });
2359
2360        // Bob does not connect the channel.
2361
2362        // Wait for the rendezvous session to time out.
2363        assert_matches!(grant.await, Err(QRCodeGrantLoginError::NotFound));
2364        updates_task.await.expect("Alice should run through all progress states");
2365    }
2366
2367    #[async_test]
2368    async fn test_grant_login_with_generated_qr_code_login_failure_instead_of_login_protocol() {
2369        let server = MatrixMockServer::new().await;
2370        let rendezvous_server = Arc::new(
2371            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await,
2372        );
2373        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
2374
2375        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
2376        server
2377            .mock_upload_cross_signing_keys()
2378            .ok()
2379            .expect(1)
2380            .named("upload_xsigning_keys")
2381            .mount()
2382            .await;
2383        server
2384            .mock_upload_cross_signing_signatures()
2385            .ok()
2386            .expect(1)
2387            .named("upload_xsigning_signatures")
2388            .mount()
2389            .await;
2390
2391        // Create the existing client (Alice).
2392        let user_id = owned_user_id!("@alice:example.org");
2393        let device_id = owned_device_id!("ALICE_DEVICE");
2394        let alice = server
2395            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
2396            .logged_in_with_oauth()
2397            .build()
2398            .await;
2399        alice
2400            .encryption()
2401            .bootstrap_cross_signing(None)
2402            .await
2403            .expect("Alice should be able to set up cross signing");
2404
2405        // Prepare the login granting future.
2406        let oauth = alice.oauth();
2407        let grant = oauth
2408            .grant_login_with_qr_code()
2409            .device_creation_timeout(Duration::from_secs(2))
2410            .generate();
2411        let (qr_code_tx, qr_code_rx) = oneshot::channel();
2412        let (checkcode_tx, checkcode_rx) = oneshot::channel();
2413
2414        // Spawn the updates task.
2415        let mut updates = grant.subscribe_to_progress();
2416        let mut state = grant.state.get();
2417        assert_matches!(state.clone(), GrantLoginProgress::Starting);
2418        let updates_task = spawn(async move {
2419            let mut qr_code_tx = Some(qr_code_tx);
2420            let mut checkcode_rx = Some(checkcode_rx);
2421
2422            while let Some(update) = updates.next().await {
2423                match &update {
2424                    GrantLoginProgress::Starting => {
2425                        assert_matches!(state, GrantLoginProgress::Starting);
2426                    }
2427                    GrantLoginProgress::EstablishingSecureChannel(
2428                        GeneratedQrProgress::QrReady(qr_code_data),
2429                    ) => {
2430                        assert_matches!(state, GrantLoginProgress::Starting);
2431                        qr_code_tx
2432                            .take()
2433                            .expect("The QR code should only be forwarded once")
2434                            .send(qr_code_data.clone())
2435                            .expect("Alice should be able to forward the QR code");
2436                    }
2437                    GrantLoginProgress::EstablishingSecureChannel(
2438                        GeneratedQrProgress::QrScanned(checkcode_sender),
2439                    ) => {
2440                        assert_matches!(
2441                            state,
2442                            GrantLoginProgress::EstablishingSecureChannel(
2443                                GeneratedQrProgress::QrReady(_)
2444                            )
2445                        );
2446                        let checkcode = checkcode_rx
2447                            .take()
2448                            .expect("The checkcode should only be forwarded once")
2449                            .await
2450                            .expect("Alice should receive the checkcode");
2451                        checkcode_sender
2452                            .send(checkcode)
2453                            .await
2454                            .expect("Alice should be able to forward the checkcode");
2455                        break;
2456                    }
2457                    _ => {
2458                        panic!("Alice should abort the process");
2459                    }
2460                }
2461                state = update;
2462            }
2463        });
2464
2465        // Let Bob request the login and run through the process.
2466        let rendezvous_server_clone = rendezvous_server.clone();
2467        let bob_task = spawn(async move {
2468            request_login_with_scanned_qr_code(
2469                BobBehaviour::LoginFailureInsteadOfLoginProtocol,
2470                qr_code_rx,
2471                checkcode_tx,
2472                None,
2473                &rendezvous_server_clone,
2474                None,
2475                None,
2476            )
2477            .await;
2478        });
2479
2480        // Wait for all tasks to finish / fail.
2481        assert_matches!(
2482            grant.await,
2483            Err(QRCodeGrantLoginError::LoginFailure { reason: LoginFailureReason::UserCancelled }),
2484            "Alice should abort the login with expected error"
2485        );
2486        updates_task.await.expect("Alice should run through all progress states");
2487        bob_task.await.expect("Bob's task should finish");
2488    }
2489
2490    #[async_test]
2491    async fn test_grant_login_with_scanned_qr_code_login_failure_instead_of_login_protocol() {
2492        let server = MatrixMockServer::new().await;
2493        let rendezvous_server = Arc::new(
2494            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await,
2495        );
2496        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
2497
2498        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
2499        server
2500            .mock_upload_cross_signing_keys()
2501            .ok()
2502            .expect(1)
2503            .named("upload_xsigning_keys")
2504            .mount()
2505            .await;
2506        server
2507            .mock_upload_cross_signing_signatures()
2508            .ok()
2509            .expect(1)
2510            .named("upload_xsigning_signatures")
2511            .mount()
2512            .await;
2513
2514        // Create a secure channel on the new client (Bob) and extract the QR
2515        // code.
2516        let client = HttpClient::new(reqwest::Client::new(), Default::default());
2517        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
2518            .await
2519            .expect("Bob should be able to create a secure channel.");
2520        let qr_code_data = channel.qr_code_data().clone();
2521
2522        // Create the existing client (Alice).
2523        let user_id = owned_user_id!("@alice:example.org");
2524        let device_id = owned_device_id!("ALICE_DEVICE");
2525        let alice = server
2526            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
2527            .logged_in_with_oauth()
2528            .build()
2529            .await;
2530        alice
2531            .encryption()
2532            .bootstrap_cross_signing(None)
2533            .await
2534            .expect("Alice should be able to set up cross signing");
2535
2536        // Prepare the login granting future using the QR code.
2537        let oauth = alice.oauth();
2538        let grant = oauth
2539            .grant_login_with_qr_code()
2540            .device_creation_timeout(Duration::from_secs(2))
2541            .scan(&qr_code_data);
2542        let (checkcode_tx, checkcode_rx) = oneshot::channel();
2543
2544        // Spawn the updates task.
2545        let mut updates = grant.subscribe_to_progress();
2546        let mut state = grant.state.get();
2547        assert_matches!(state.clone(), GrantLoginProgress::Starting);
2548        let updates_task = spawn(async move {
2549            let mut checkcode_tx = Some(checkcode_tx);
2550
2551            while let Some(update) = updates.next().await {
2552                match &update {
2553                    GrantLoginProgress::Starting => {
2554                        assert_matches!(state, GrantLoginProgress::Starting);
2555                    }
2556                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
2557                        assert_matches!(state, GrantLoginProgress::Starting);
2558                        checkcode_tx
2559                            .take()
2560                            .expect("The checkcode should only be forwarded once")
2561                            .send(*check_code)
2562                            .expect("Alice should be able to forward the checkcode");
2563                        break;
2564                    }
2565                    _ => {
2566                        panic!("Alice should abort the process");
2567                    }
2568                }
2569                state = update;
2570            }
2571        });
2572
2573        let rendezvous_server_clone = rendezvous_server.clone();
2574        // Let Bob request the login and run through the process.
2575        let bob_task = spawn(async move {
2576            request_login_with_generated_qr_code(
2577                BobBehaviour::LoginFailureInsteadOfLoginProtocol,
2578                channel,
2579                checkcode_rx,
2580                None,
2581                &rendezvous_server_clone,
2582                alice.homeserver(),
2583                None,
2584                None,
2585            )
2586            .await;
2587        });
2588
2589        // Wait for all tasks to finish / fail.
2590        assert_matches!(
2591            grant.await,
2592            Err(QRCodeGrantLoginError::LoginFailure { reason: LoginFailureReason::UserCancelled }),
2593            "Alice should abort the login with expected error"
2594        );
2595        updates_task.await.expect("Alice should run through all progress states");
2596        bob_task.await.expect("Bob's task should finish");
2597    }
2598
2599    #[async_test]
2600    async fn test_grant_login_with_scanned_qr_code_login_failure_instead_of_login_success() {
2601        let server = MatrixMockServer::new().await;
2602        let rendezvous_server = Arc::new(
2603            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await,
2604        );
2605        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
2606
2607        let device_authorization_grant = AuthorizationGrant {
2608            verification_uri_complete: Some(VerificationUriComplete::new(
2609                "https://id.matrix.org/device/abcde".to_owned(),
2610            )),
2611            verification_uri: EndUserVerificationUrl::new(
2612                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
2613            )
2614            .unwrap(),
2615        };
2616
2617        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
2618        server
2619            .mock_upload_cross_signing_keys()
2620            .ok()
2621            .expect(1)
2622            .named("upload_xsigning_keys")
2623            .mount()
2624            .await;
2625        server
2626            .mock_upload_cross_signing_signatures()
2627            .ok()
2628            .expect(1)
2629            .named("upload_xsigning_signatures")
2630            .mount()
2631            .await;
2632
2633        // Create the existing client (Alice).
2634        let user_id = owned_user_id!("@alice:example.org");
2635        let device_id = owned_device_id!("ALICE_DEVICE");
2636        let alice = server
2637            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
2638            .logged_in_with_oauth()
2639            .build()
2640            .await;
2641        alice
2642            .encryption()
2643            .bootstrap_cross_signing(None)
2644            .await
2645            .expect("Alice should be able to set up cross signing");
2646
2647        // Prepare the login granting future.
2648        let oauth = alice.oauth();
2649        let grant = oauth
2650            .grant_login_with_qr_code()
2651            .device_creation_timeout(Duration::from_secs(2))
2652            .generate();
2653        let (qr_code_tx, qr_code_rx) = oneshot::channel();
2654        let (checkcode_tx, checkcode_rx) = oneshot::channel();
2655
2656        // Spawn the updates task.
2657        let mut updates = grant.subscribe_to_progress();
2658        let mut state = grant.state.get();
2659        let verification_uri_complete =
2660            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
2661        assert_matches!(state.clone(), GrantLoginProgress::Starting);
2662        let updates_task = spawn(async move {
2663            let mut qr_code_tx = Some(qr_code_tx);
2664            let mut checkcode_rx = Some(checkcode_rx);
2665
2666            while let Some(update) = updates.next().await {
2667                match &update {
2668                    GrantLoginProgress::Starting => {
2669                        assert_matches!(state, GrantLoginProgress::Starting);
2670                    }
2671                    GrantLoginProgress::EstablishingSecureChannel(
2672                        GeneratedQrProgress::QrReady(qr_code_data),
2673                    ) => {
2674                        assert_matches!(state, GrantLoginProgress::Starting);
2675                        qr_code_tx
2676                            .take()
2677                            .expect("The QR code should only be forwarded once")
2678                            .send(qr_code_data.clone())
2679                            .expect("Alice should be able to forward the QR code");
2680                    }
2681                    GrantLoginProgress::EstablishingSecureChannel(
2682                        GeneratedQrProgress::QrScanned(checkcode_sender),
2683                    ) => {
2684                        assert_matches!(
2685                            state,
2686                            GrantLoginProgress::EstablishingSecureChannel(
2687                                GeneratedQrProgress::QrReady(_)
2688                            )
2689                        );
2690                        let checkcode = checkcode_rx
2691                            .take()
2692                            .expect("The checkcode should only be forwarded once")
2693                            .await
2694                            .expect("Alice should receive the checkcode");
2695                        checkcode_sender
2696                            .send(checkcode)
2697                            .await
2698                            .expect("Alice should be able to forward the checkcode");
2699                    }
2700                    GrantLoginProgress::WaitingForAuth {
2701                        verification_uri,
2702                        continuation_sender,
2703                    } => {
2704                        assert_matches!(
2705                            state,
2706                            GrantLoginProgress::EstablishingSecureChannel(
2707                                GeneratedQrProgress::QrScanned(_)
2708                            )
2709                        );
2710                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
2711                        continuation_sender.confirm().await.expect("should be able to confirm");
2712                    }
2713                    _ => {
2714                        panic!("Alice should abort the process");
2715                    }
2716                }
2717                state = update;
2718            }
2719        });
2720
2721        let rendezvous_server_clone = rendezvous_server.clone();
2722        // Let Bob request the login and run through the process.
2723        let bob_task = spawn(async move {
2724            request_login_with_scanned_qr_code(
2725                BobBehaviour::LoginFailureInsteadOfLoginSuccess,
2726                qr_code_rx,
2727                checkcode_tx,
2728                None,
2729                &rendezvous_server_clone,
2730                Some(device_authorization_grant),
2731                None,
2732            )
2733            .await;
2734        });
2735
2736        // Wait for all tasks to finish / fail.
2737        assert_matches!(
2738            grant.await,
2739            Err(QRCodeGrantLoginError::LoginFailure {
2740                reason: LoginFailureReason::AuthorizationExpired
2741            }),
2742            "Alice should abort the login with expected error"
2743        );
2744        updates_task.await.expect("Alice should run through all progress states");
2745        bob_task.await.expect("Bob's task should finish");
2746    }
2747
2748    #[async_test]
2749    async fn test_grant_login_with_generated_qr_code_login_failure_instead_of_login_success() {
2750        let server = MatrixMockServer::new().await;
2751        let rendezvous_server = Arc::new(
2752            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await,
2753        );
2754        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
2755
2756        let device_authorization_grant = AuthorizationGrant {
2757            verification_uri_complete: Some(VerificationUriComplete::new(
2758                "https://id.matrix.org/device/abcde".to_owned(),
2759            )),
2760            verification_uri: EndUserVerificationUrl::new(
2761                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
2762            )
2763            .unwrap(),
2764        };
2765
2766        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
2767        server
2768            .mock_upload_cross_signing_keys()
2769            .ok()
2770            .expect(1)
2771            .named("upload_xsigning_keys")
2772            .mount()
2773            .await;
2774        server
2775            .mock_upload_cross_signing_signatures()
2776            .ok()
2777            .expect(1)
2778            .named("upload_xsigning_signatures")
2779            .mount()
2780            .await;
2781
2782        // Create a secure channel on the new client (Bob) and extract the QR
2783        // code.
2784        let client = HttpClient::new(reqwest::Client::new(), Default::default());
2785        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
2786            .await
2787            .expect("Bob should be able to create a secure channel.");
2788        let qr_code_data = channel.qr_code_data().clone();
2789
2790        // Create the existing client (Alice).
2791        let user_id = owned_user_id!("@alice:example.org");
2792        let device_id = owned_device_id!("ALICE_DEVICE");
2793        let alice = server
2794            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
2795            .logged_in_with_oauth()
2796            .build()
2797            .await;
2798        alice
2799            .encryption()
2800            .bootstrap_cross_signing(None)
2801            .await
2802            .expect("Alice should be able to set up cross signing");
2803
2804        // Prepare the login granting future using the QR code.
2805        let oauth = alice.oauth();
2806        let grant = oauth
2807            .grant_login_with_qr_code()
2808            .device_creation_timeout(Duration::from_secs(2))
2809            .scan(&qr_code_data);
2810        let (checkcode_tx, checkcode_rx) = oneshot::channel();
2811
2812        // Spawn the updates task.
2813        let mut updates = grant.subscribe_to_progress();
2814        let mut state = grant.state.get();
2815        let verification_uri_complete =
2816            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
2817        assert_matches!(state.clone(), GrantLoginProgress::Starting);
2818        let updates_task = spawn(async move {
2819            let mut checkcode_tx = Some(checkcode_tx);
2820
2821            while let Some(update) = updates.next().await {
2822                match &update {
2823                    GrantLoginProgress::Starting => {
2824                        assert_matches!(state, GrantLoginProgress::Starting);
2825                    }
2826                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
2827                        assert_matches!(state, GrantLoginProgress::Starting);
2828                        checkcode_tx
2829                            .take()
2830                            .expect("The checkcode should only be forwarded once")
2831                            .send(*check_code)
2832                            .expect("Alice should be able to forward the checkcode");
2833                    }
2834                    GrantLoginProgress::WaitingForAuth {
2835                        verification_uri,
2836                        continuation_sender,
2837                    } => {
2838                        assert_matches!(
2839                            state,
2840                            GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
2841                        );
2842                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
2843                        continuation_sender.confirm().await.expect("should be able to confirm");
2844                    }
2845                    _ => {
2846                        panic!("Alice should abort the process");
2847                    }
2848                }
2849                state = update;
2850            }
2851        });
2852
2853        let rendezvous_server_clone = rendezvous_server.clone();
2854        // Let Bob request the login and run through the process.
2855        let bob_task = spawn(async move {
2856            request_login_with_generated_qr_code(
2857                BobBehaviour::LoginFailureInsteadOfLoginSuccess,
2858                channel,
2859                checkcode_rx,
2860                None,
2861                &rendezvous_server_clone,
2862                alice.homeserver(),
2863                Some(device_authorization_grant),
2864                None,
2865            )
2866            .await;
2867        });
2868
2869        // Wait for all tasks to finish / fail.
2870        assert_matches!(
2871            grant.await,
2872            Err(QRCodeGrantLoginError::LoginFailure {
2873                reason: LoginFailureReason::AuthorizationExpired
2874            }),
2875            "Alice should abort the login with expected error"
2876        );
2877        updates_task.await.expect("Alice should run through all progress states");
2878        bob_task.await.expect("Bob's task should finish");
2879    }
2880
2881    #[async_test]
2882    async fn test_grant_login_with_generated_qr_code_unexpected_message_instead_of_login_success() {
2883        let server = MatrixMockServer::new().await;
2884        let rendezvous_server = Arc::new(
2885            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await,
2886        );
2887        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
2888
2889        let device_authorization_grant = AuthorizationGrant {
2890            verification_uri_complete: Some(VerificationUriComplete::new(
2891                "https://id.matrix.org/device/abcde".to_owned(),
2892            )),
2893            verification_uri: EndUserVerificationUrl::new(
2894                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
2895            )
2896            .unwrap(),
2897        };
2898
2899        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
2900        server
2901            .mock_upload_cross_signing_keys()
2902            .ok()
2903            .expect(1)
2904            .named("upload_xsigning_keys")
2905            .mount()
2906            .await;
2907        server
2908            .mock_upload_cross_signing_signatures()
2909            .ok()
2910            .expect(1)
2911            .named("upload_xsigning_signatures")
2912            .mount()
2913            .await;
2914
2915        // Create the existing client (Alice).
2916        let user_id = owned_user_id!("@alice:example.org");
2917        let device_id = owned_device_id!("ALICE_DEVICE");
2918        let alice = server
2919            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
2920            .logged_in_with_oauth()
2921            .build()
2922            .await;
2923        alice
2924            .encryption()
2925            .bootstrap_cross_signing(None)
2926            .await
2927            .expect("Alice should be able to set up cross signing");
2928
2929        // Prepare the login granting future.
2930        let oauth = alice.oauth();
2931        let grant = oauth
2932            .grant_login_with_qr_code()
2933            .device_creation_timeout(Duration::from_secs(2))
2934            .generate();
2935        let (qr_code_tx, qr_code_rx) = oneshot::channel();
2936        let (checkcode_tx, checkcode_rx) = oneshot::channel();
2937
2938        // Spawn the updates task.
2939        let mut updates = grant.subscribe_to_progress();
2940        let mut state = grant.state.get();
2941        let verification_uri_complete =
2942            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
2943        assert_matches!(state.clone(), GrantLoginProgress::Starting);
2944        let updates_task = spawn(async move {
2945            let mut qr_code_tx = Some(qr_code_tx);
2946            let mut checkcode_rx = Some(checkcode_rx);
2947
2948            while let Some(update) = updates.next().await {
2949                match &update {
2950                    GrantLoginProgress::Starting => {
2951                        assert_matches!(state, GrantLoginProgress::Starting);
2952                    }
2953                    GrantLoginProgress::EstablishingSecureChannel(
2954                        GeneratedQrProgress::QrReady(qr_code_data),
2955                    ) => {
2956                        assert_matches!(state, GrantLoginProgress::Starting);
2957                        qr_code_tx
2958                            .take()
2959                            .expect("The QR code should only be forwarded once")
2960                            .send(qr_code_data.clone())
2961                            .expect("Alice should be able to forward the QR code");
2962                    }
2963                    GrantLoginProgress::EstablishingSecureChannel(
2964                        GeneratedQrProgress::QrScanned(checkcode_sender),
2965                    ) => {
2966                        assert_matches!(
2967                            state,
2968                            GrantLoginProgress::EstablishingSecureChannel(
2969                                GeneratedQrProgress::QrReady(_)
2970                            )
2971                        );
2972                        let checkcode = checkcode_rx
2973                            .take()
2974                            .expect("The checkcode should only be forwarded once")
2975                            .await
2976                            .expect("Alice should receive the checkcode");
2977                        checkcode_sender
2978                            .send(checkcode)
2979                            .await
2980                            .expect("Alice should be able to forward the checkcode");
2981                    }
2982                    GrantLoginProgress::WaitingForAuth {
2983                        verification_uri,
2984                        continuation_sender,
2985                    } => {
2986                        assert_matches!(
2987                            state,
2988                            GrantLoginProgress::EstablishingSecureChannel(
2989                                GeneratedQrProgress::QrScanned(_)
2990                            )
2991                        );
2992                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
2993                        continuation_sender.confirm().await.expect("should be able to confirm");
2994                    }
2995                    _ => {
2996                        panic!("Alice should abort the process");
2997                    }
2998                }
2999                state = update;
3000            }
3001        });
3002
3003        // Let Bob request the login and run through the process.
3004        let rendezvous_server_clone = rendezvous_server.clone();
3005        let bob_task = spawn(async move {
3006            request_login_with_scanned_qr_code(
3007                BobBehaviour::UnexpectedMessageInsteadOfLoginSuccess,
3008                qr_code_rx,
3009                checkcode_tx,
3010                None,
3011                &rendezvous_server_clone,
3012                Some(device_authorization_grant),
3013                None,
3014            )
3015            .await;
3016        });
3017
3018        // Wait for all tasks to finish / fail.
3019        assert_let!(
3020            Err(QRCodeGrantLoginError::UnexpectedMessage {
3021                expected: "m.login.success",
3022                received,
3023            }) = grant.await,
3024            "Alice should abort the login with expected error variant"
3025        );
3026        assert_matches!(
3027            *received,
3028            QrAuthMessage::LoginProtocolAccepted,
3029            "Alice should abort the login with expected error message"
3030        );
3031        updates_task.await.expect("Alice should run through all progress states");
3032        bob_task.await.expect("Bob's task should finish");
3033    }
3034
3035    #[async_test]
3036    async fn test_grant_login_with_scanned_qr_code_unexpected_message_instead_of_login_success() {
3037        let server = MatrixMockServer::new().await;
3038        let rendezvous_server = Arc::new(
3039            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await,
3040        );
3041        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
3042
3043        let device_authorization_grant = AuthorizationGrant {
3044            verification_uri_complete: Some(VerificationUriComplete::new(
3045                "https://id.matrix.org/device/abcde".to_owned(),
3046            )),
3047            verification_uri: EndUserVerificationUrl::new(
3048                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
3049            )
3050            .unwrap(),
3051        };
3052
3053        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
3054        server
3055            .mock_upload_cross_signing_keys()
3056            .ok()
3057            .expect(1)
3058            .named("upload_xsigning_keys")
3059            .mount()
3060            .await;
3061        server
3062            .mock_upload_cross_signing_signatures()
3063            .ok()
3064            .expect(1)
3065            .named("upload_xsigning_signatures")
3066            .mount()
3067            .await;
3068
3069        // Create a secure channel on the new client (Bob) and extract the QR
3070        // code.
3071        let client = HttpClient::new(reqwest::Client::new(), Default::default());
3072        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
3073            .await
3074            .expect("Bob should be able to create a secure channel.");
3075        let qr_code_data = channel.qr_code_data().clone();
3076
3077        // Create the existing client (Alice).
3078        let user_id = owned_user_id!("@alice:example.org");
3079        let device_id = owned_device_id!("ALICE_DEVICE");
3080        let alice = server
3081            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
3082            .logged_in_with_oauth()
3083            .build()
3084            .await;
3085        alice
3086            .encryption()
3087            .bootstrap_cross_signing(None)
3088            .await
3089            .expect("Alice should be able to set up cross signing");
3090
3091        // Prepare the login granting future using the QR code.
3092        let oauth = alice.oauth();
3093        let grant = oauth
3094            .grant_login_with_qr_code()
3095            .device_creation_timeout(Duration::from_secs(2))
3096            .scan(&qr_code_data);
3097        let (checkcode_tx, checkcode_rx) = oneshot::channel();
3098
3099        // Spawn the updates task.
3100        let mut updates = grant.subscribe_to_progress();
3101        let mut state = grant.state.get();
3102        let verification_uri_complete =
3103            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
3104        assert_matches!(state.clone(), GrantLoginProgress::Starting);
3105        let updates_task = spawn(async move {
3106            let mut checkcode_tx = Some(checkcode_tx);
3107
3108            while let Some(update) = updates.next().await {
3109                match &update {
3110                    GrantLoginProgress::Starting => {
3111                        assert_matches!(state, GrantLoginProgress::Starting);
3112                    }
3113                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
3114                        assert_matches!(state, GrantLoginProgress::Starting);
3115                        checkcode_tx
3116                            .take()
3117                            .expect("The checkcode should only be forwarded once")
3118                            .send(*check_code)
3119                            .expect("Alice should be able to forward the checkcode");
3120                    }
3121                    GrantLoginProgress::WaitingForAuth {
3122                        verification_uri,
3123                        continuation_sender,
3124                    } => {
3125                        assert_matches!(
3126                            state,
3127                            GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
3128                        );
3129                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
3130                        continuation_sender.confirm().await.expect("should be able to confirm");
3131                    }
3132                    _ => {
3133                        panic!("Alice should abort the process");
3134                    }
3135                }
3136                state = update;
3137            }
3138        });
3139
3140        let rendezvous_server_clone = rendezvous_server.clone();
3141        // Let Bob request the login and run through the process.
3142        let bob_task = spawn(async move {
3143            request_login_with_generated_qr_code(
3144                BobBehaviour::UnexpectedMessageInsteadOfLoginSuccess,
3145                channel,
3146                checkcode_rx,
3147                None,
3148                &rendezvous_server_clone,
3149                alice.homeserver(),
3150                Some(device_authorization_grant),
3151                None,
3152            )
3153            .await;
3154        });
3155
3156        // Wait for all tasks to finish / fail.
3157        assert_let!(
3158            Err(QRCodeGrantLoginError::UnexpectedMessage {
3159                expected: "m.login.success",
3160                received,
3161            }) = grant.await,
3162            "Alice should abort the login with expected error variant"
3163        );
3164        assert_matches!(
3165            *received,
3166            QrAuthMessage::LoginProtocolAccepted,
3167            "Alice should abort the login with expected error message"
3168        );
3169        updates_task.await.expect("Alice should run through all progress states");
3170        bob_task.await.expect("Bob's task should finish");
3171    }
3172
3173    #[async_test]
3174    async fn test_grant_login_with_generated_qr_code_secure_channel_error() {
3175        let server = MatrixMockServer::new().await;
3176        let rendezvous_server = Arc::new(
3177            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await,
3178        );
3179        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
3180
3181        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
3182        server
3183            .mock_upload_cross_signing_keys()
3184            .ok()
3185            .expect(1)
3186            .named("upload_xsigning_keys")
3187            .mount()
3188            .await;
3189        server
3190            .mock_upload_cross_signing_signatures()
3191            .ok()
3192            .expect(1)
3193            .named("upload_xsigning_signatures")
3194            .mount()
3195            .await;
3196
3197        // Create the existing client (Alice).
3198        let user_id = owned_user_id!("@alice:example.org");
3199        let device_id = owned_device_id!("ALICE_DEVICE");
3200        let alice = server
3201            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
3202            .logged_in_with_oauth()
3203            .build()
3204            .await;
3205        alice
3206            .encryption()
3207            .bootstrap_cross_signing(None)
3208            .await
3209            .expect("Alice should be able to set up cross signing");
3210
3211        // Prepare the login granting future.
3212        let oauth = alice.oauth();
3213        let grant = oauth
3214            .grant_login_with_qr_code()
3215            .device_creation_timeout(Duration::from_secs(2))
3216            .generate();
3217        let (qr_code_tx, qr_code_rx) = oneshot::channel();
3218        let (checkcode_tx, checkcode_rx) = oneshot::channel();
3219
3220        // Spawn the updates task.
3221        let mut updates = grant.subscribe_to_progress();
3222        let mut state = grant.state.get();
3223        assert_matches!(state.clone(), GrantLoginProgress::Starting);
3224        let updates_task = spawn(async move {
3225            let mut qr_code_tx = Some(qr_code_tx);
3226            let mut checkcode_rx = Some(checkcode_rx);
3227
3228            while let Some(update) = updates.next().await {
3229                match &update {
3230                    GrantLoginProgress::Starting => {
3231                        assert_matches!(state, GrantLoginProgress::Starting);
3232                    }
3233                    GrantLoginProgress::EstablishingSecureChannel(
3234                        GeneratedQrProgress::QrReady(qr_code_data),
3235                    ) => {
3236                        assert_matches!(state, GrantLoginProgress::Starting);
3237                        qr_code_tx
3238                            .take()
3239                            .expect("The QR code should only be forwarded once")
3240                            .send(qr_code_data.clone())
3241                            .expect("Alice should be able to forward the QR code");
3242                    }
3243                    GrantLoginProgress::EstablishingSecureChannel(
3244                        GeneratedQrProgress::QrScanned(checkcode_sender),
3245                    ) => {
3246                        assert_matches!(
3247                            state,
3248                            GrantLoginProgress::EstablishingSecureChannel(
3249                                GeneratedQrProgress::QrReady(_)
3250                            )
3251                        );
3252                        let checkcode = checkcode_rx
3253                            .take()
3254                            .expect("The checkcode should only be forwarded once")
3255                            .await
3256                            .expect("Alice should receive the checkcode");
3257                        checkcode_sender
3258                            .send(checkcode)
3259                            .await
3260                            .expect("Alice should be able to forward the checkcode");
3261                        break;
3262                    }
3263                    _ => {
3264                        panic!("Alice should abort the process");
3265                    }
3266                }
3267                state = update;
3268            }
3269        });
3270
3271        // Let Bob request the login and run through the process.
3272        let rendezvous_server_clone = rendezvous_server.clone();
3273        let bob_task = spawn(async move {
3274            request_login_with_scanned_qr_code(
3275                BobBehaviour::InvalidJsonMessage,
3276                qr_code_rx,
3277                checkcode_tx,
3278                None,
3279                &rendezvous_server_clone,
3280                None,
3281                None,
3282            )
3283            .await;
3284        });
3285
3286        // Wait for all tasks to finish / fail.
3287        assert_matches!(
3288            grant.await,
3289            Err(QRCodeGrantLoginError::SecureChannel(SecureChannelError::MessageDecode(
3290                MessageDecodeError::Json(_)
3291            ))),
3292            "Alice should abort the login with a SecureChannel error"
3293        );
3294        updates_task.await.expect("Alice should run through all progress states");
3295        bob_task.await.expect("Bob's task should finish");
3296    }
3297
3298    #[async_test]
3299    async fn test_grant_login_with_scanned_qr_code_secure_channel_error() {
3300        let server = MatrixMockServer::new().await;
3301        let rendezvous_server = Arc::new(
3302            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await,
3303        );
3304        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
3305
3306        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
3307        server
3308            .mock_upload_cross_signing_keys()
3309            .ok()
3310            .expect(1)
3311            .named("upload_xsigning_keys")
3312            .mount()
3313            .await;
3314        server
3315            .mock_upload_cross_signing_signatures()
3316            .ok()
3317            .expect(1)
3318            .named("upload_xsigning_signatures")
3319            .mount()
3320            .await;
3321
3322        // Create a secure channel on the new client (Bob) and extract the QR
3323        // code.
3324        let client = HttpClient::new(reqwest::Client::new(), Default::default());
3325        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
3326            .await
3327            .expect("Bob should be able to create a secure channel.");
3328        let qr_code_data = channel.qr_code_data().clone();
3329
3330        // Create the existing client (Alice).
3331        let user_id = owned_user_id!("@alice:example.org");
3332        let device_id = owned_device_id!("ALICE_DEVICE");
3333        let alice = server
3334            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
3335            .logged_in_with_oauth()
3336            .build()
3337            .await;
3338        alice
3339            .encryption()
3340            .bootstrap_cross_signing(None)
3341            .await
3342            .expect("Alice should be able to set up cross signing");
3343
3344        // Prepare the login granting future using the QR code.
3345        let oauth = alice.oauth();
3346        let grant = oauth
3347            .grant_login_with_qr_code()
3348            .device_creation_timeout(Duration::from_secs(2))
3349            .scan(&qr_code_data);
3350        let (checkcode_tx, checkcode_rx) = oneshot::channel();
3351
3352        // Spawn the updates task.
3353        let mut updates = grant.subscribe_to_progress();
3354        let mut state = grant.state.get();
3355        assert_matches!(state.clone(), GrantLoginProgress::Starting);
3356        let updates_task = spawn(async move {
3357            let mut checkcode_tx = Some(checkcode_tx);
3358
3359            while let Some(update) = updates.next().await {
3360                match &update {
3361                    GrantLoginProgress::Starting => {
3362                        assert_matches!(state, GrantLoginProgress::Starting);
3363                    }
3364                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
3365                        assert_matches!(state, GrantLoginProgress::Starting);
3366                        checkcode_tx
3367                            .take()
3368                            .expect("The checkcode should only be forwarded once")
3369                            .send(*check_code)
3370                            .expect("Alice should be able to forward the checkcode");
3371                        break;
3372                    }
3373                    _ => {
3374                        panic!("Alice should abort the process");
3375                    }
3376                }
3377                state = update;
3378            }
3379        });
3380
3381        let rendezvous_server_clone = rendezvous_server.clone();
3382        // Let Bob request the login and run through the process.
3383        let bob_task = spawn(async move {
3384            request_login_with_generated_qr_code(
3385                BobBehaviour::InvalidJsonMessage,
3386                channel,
3387                checkcode_rx,
3388                None,
3389                &rendezvous_server_clone,
3390                alice.homeserver(),
3391                None,
3392                None,
3393            )
3394            .await;
3395        });
3396
3397        // Wait for all tasks to finish / fail.
3398        assert_matches!(
3399            grant.await,
3400            Err(QRCodeGrantLoginError::SecureChannel(SecureChannelError::MessageDecode(
3401                MessageDecodeError::Json(_)
3402            ))),
3403            "Alice should abort the login with a SecureChannel error"
3404        );
3405        updates_task.await.expect("Alice should run through all progress states");
3406        bob_task.await.expect("Bob's task should finish");
3407    }
3408
3409    #[async_test]
3410    async fn test_grant_login_with_generated_qr_code_cancelled_while_waiting_for_auth() {
3411        let server = MatrixMockServer::new().await;
3412        let rendezvous_server =
3413            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
3414        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
3415
3416        let device_authorization_grant = AuthorizationGrant {
3417            verification_uri_complete: Some(VerificationUriComplete::new(
3418                "https://id.matrix.org/device/abcde".to_owned(),
3419            )),
3420            verification_uri: EndUserVerificationUrl::new(
3421                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
3422            )
3423            .unwrap(),
3424        };
3425
3426        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
3427        server
3428            .mock_upload_cross_signing_keys()
3429            .ok()
3430            .expect(1)
3431            .named("upload_xsigning_keys")
3432            .mount()
3433            .await;
3434        server
3435            .mock_upload_cross_signing_signatures()
3436            .ok()
3437            .expect(1)
3438            .named("upload_xsigning_signatures")
3439            .mount()
3440            .await;
3441
3442        // Create the existing client (Alice).
3443        let user_id = owned_user_id!("@alice:example.org");
3444        let device_id = owned_device_id!("ALICE_DEVICE");
3445        let alice = server
3446            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
3447            .logged_in_with_oauth()
3448            .build()
3449            .await;
3450        alice
3451            .encryption()
3452            .bootstrap_cross_signing(None)
3453            .await
3454            .expect("Alice should be able to set up cross signing");
3455
3456        // Prepare the login granting future.
3457        let oauth = alice.oauth();
3458        let grant = oauth
3459            .grant_login_with_qr_code()
3460            .device_creation_timeout(Duration::from_secs(2))
3461            .generate();
3462        let (qr_code_tx, qr_code_rx) = oneshot::channel();
3463        let (checkcode_tx, checkcode_rx) = oneshot::channel();
3464
3465        // Spawn the updates task.
3466        let mut updates = grant.subscribe_to_progress();
3467        let mut state = grant.state.get();
3468        let verification_uri_complete =
3469            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
3470        assert_matches!(state.clone(), GrantLoginProgress::Starting);
3471        let updates_task = spawn(async move {
3472            let mut qr_code_tx = Some(qr_code_tx);
3473            let mut checkcode_rx = Some(checkcode_rx);
3474
3475            while let Some(update) = updates.next().await {
3476                match &update {
3477                    GrantLoginProgress::Starting => {
3478                        assert_matches!(state, GrantLoginProgress::Starting);
3479                    }
3480                    GrantLoginProgress::EstablishingSecureChannel(
3481                        GeneratedQrProgress::QrReady(qr_code_data),
3482                    ) => {
3483                        assert_matches!(state, GrantLoginProgress::Starting);
3484                        qr_code_tx
3485                            .take()
3486                            .expect("The QR code should only be forwarded once")
3487                            .send(qr_code_data.clone())
3488                            .expect("Alice should be able to forward the QR code");
3489                    }
3490                    GrantLoginProgress::EstablishingSecureChannel(
3491                        GeneratedQrProgress::QrScanned(checkcode_sender),
3492                    ) => {
3493                        assert_matches!(
3494                            state,
3495                            GrantLoginProgress::EstablishingSecureChannel(
3496                                GeneratedQrProgress::QrReady(_)
3497                            )
3498                        );
3499                        let checkcode = checkcode_rx
3500                            .take()
3501                            .expect("The checkcode should only be forwarded once")
3502                            .await
3503                            .expect("Alice should receive the checkcode");
3504                        checkcode_sender
3505                            .send(checkcode)
3506                            .await
3507                            .expect("Alice should be able to forward the checkcode");
3508                    }
3509                    GrantLoginProgress::WaitingForAuth {
3510                        verification_uri,
3511                        continuation_sender,
3512                    } => {
3513                        assert_matches!(
3514                            state,
3515                            GrantLoginProgress::EstablishingSecureChannel(
3516                                GeneratedQrProgress::QrScanned(_)
3517                            )
3518                        );
3519                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
3520                        // The user cancels the login instead of confirming it.
3521                        continuation_sender.cancel().await.expect("should be able to cancel");
3522                        break;
3523                    }
3524                    _ => {
3525                        panic!("Alice should abort the process after being cancelled");
3526                    }
3527                }
3528                state = update;
3529            }
3530        });
3531
3532        // Let Bob request the login and run through the process.
3533        let bob_task = spawn(async move {
3534            request_login_with_scanned_qr_code(
3535                BobBehaviour::CancelledWhileWaitingForAuth,
3536                qr_code_rx,
3537                checkcode_tx,
3538                Some(server),
3539                &rendezvous_server,
3540                Some(device_authorization_grant),
3541                None,
3542            )
3543            .await;
3544        });
3545
3546        // Wait for all tasks to finish / fail.
3547        assert_matches!(
3548            grant.await,
3549            Err(QRCodeGrantLoginError::LoginFailure { reason: LoginFailureReason::UserCancelled }),
3550            "Alice should abort the login with expected error"
3551        );
3552        updates_task.await.expect("Alice should run through all progress states");
3553        bob_task.await.expect("Bob's task should finish");
3554    }
3555
3556    #[async_test]
3557    async fn test_grant_login_with_scanned_qr_code_cancelled_while_waiting_for_auth() {
3558        let server = MatrixMockServer::new().await;
3559        let rendezvous_server =
3560            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
3561        debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
3562
3563        let device_authorization_grant = AuthorizationGrant {
3564            verification_uri_complete: Some(VerificationUriComplete::new(
3565                "https://id.matrix.org/device/abcde".to_owned(),
3566            )),
3567            verification_uri: EndUserVerificationUrl::new(
3568                "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
3569            )
3570            .unwrap(),
3571        };
3572
3573        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
3574        server
3575            .mock_upload_cross_signing_keys()
3576            .ok()
3577            .expect(1)
3578            .named("upload_xsigning_keys")
3579            .mount()
3580            .await;
3581        server
3582            .mock_upload_cross_signing_signatures()
3583            .ok()
3584            .expect(1)
3585            .named("upload_xsigning_signatures")
3586            .mount()
3587            .await;
3588
3589        // Create a secure channel on the new client (Bob) and extract the QR
3590        // code.
3591        let client = HttpClient::new(reqwest::Client::new(), Default::default());
3592        let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
3593            .await
3594            .expect("Bob should be able to create a secure channel.");
3595        let qr_code_data = channel.qr_code_data().clone();
3596
3597        // Create the existing client (Alice).
3598        let user_id = owned_user_id!("@alice:example.org");
3599        let device_id = owned_device_id!("ALICE_DEVICE");
3600        let alice = server
3601            .client_builder_for_crypto_end_to_end(&user_id, &device_id)
3602            .logged_in_with_oauth()
3603            .build()
3604            .await;
3605        alice
3606            .encryption()
3607            .bootstrap_cross_signing(None)
3608            .await
3609            .expect("Alice should be able to set up cross signing");
3610
3611        // Prepare the login granting future using the QR code.
3612        let oauth = alice.oauth();
3613        let grant = oauth
3614            .grant_login_with_qr_code()
3615            .device_creation_timeout(Duration::from_secs(2))
3616            .scan(&qr_code_data);
3617        let (checkcode_tx, checkcode_rx) = oneshot::channel();
3618
3619        // Spawn the updates task.
3620        let mut updates = grant.subscribe_to_progress();
3621        let mut state = grant.state.get();
3622        let verification_uri_complete =
3623            device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
3624        assert_matches!(state.clone(), GrantLoginProgress::Starting);
3625        let updates_task = spawn(async move {
3626            let mut checkcode_tx = Some(checkcode_tx);
3627
3628            while let Some(update) = updates.next().await {
3629                match &update {
3630                    GrantLoginProgress::Starting => {
3631                        assert_matches!(state, GrantLoginProgress::Starting);
3632                    }
3633                    GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
3634                        assert_matches!(state, GrantLoginProgress::Starting);
3635                        checkcode_tx
3636                            .take()
3637                            .expect("The checkcode should only be forwarded once")
3638                            .send(*check_code)
3639                            .expect("Alice should be able to forward the checkcode");
3640                    }
3641                    GrantLoginProgress::WaitingForAuth {
3642                        verification_uri,
3643                        continuation_sender,
3644                    } => {
3645                        assert_matches!(
3646                            state,
3647                            GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
3648                        );
3649                        assert_eq!(verification_uri.as_str(), verification_uri_complete);
3650                        // The user cancels the login instead of confirming it.
3651                        continuation_sender.cancel().await.expect("should be able to cancel");
3652                        break;
3653                    }
3654                    _ => {
3655                        panic!("Alice should abort the process after being cancelled");
3656                    }
3657                }
3658                state = update;
3659            }
3660        });
3661
3662        // Let Bob request the login and run through the process.
3663        let bob_task = spawn(async move {
3664            request_login_with_generated_qr_code(
3665                BobBehaviour::CancelledWhileWaitingForAuth,
3666                channel,
3667                checkcode_rx,
3668                Some(server),
3669                &rendezvous_server,
3670                alice.homeserver(),
3671                Some(device_authorization_grant),
3672                None,
3673            )
3674            .await;
3675        });
3676
3677        // Wait for all tasks to finish / fail.
3678        assert_matches!(
3679            grant.await,
3680            Err(QRCodeGrantLoginError::LoginFailure { reason: LoginFailureReason::UserCancelled }),
3681            "Alice should abort the login with expected error"
3682        );
3683        updates_task.await.expect("Alice should run through all progress states");
3684        bob_task.await.expect("Bob's task should finish");
3685    }
3686}