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