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