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