1use 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 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 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 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 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 let message = QrAuthMessage::LoginProtocolAccepted;
118 channel.send_json(&message).await?;
119
120 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 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 Instant::now() < deadline {
144 tokio::time::sleep(Duration::from_millis(500)).await;
145 continue;
146 } else {
147 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 state.set(GrantLoginProgress::SyncingSecrets);
162 let message = QrAuthMessage::LoginSecrets(secrets_bundle.clone());
163 channel.send_json(&message).await?;
164
165 state.set(GrantLoginProgress::Done);
167
168 Ok(())
169}
170
171#[derive(Clone, Debug, Default)]
173pub enum GrantLoginProgress<Q> {
174 #[default]
176 Starting,
177 EstablishingSecureChannel(Q),
180 WaitingForAuth {
183 verification_uri: Url,
185 },
186 SyncingSecrets,
189 Done,
191}
192
193#[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 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 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 let check_code = channel.check_code().to_owned();
256 self.state
257 .set(GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }));
258
259 let message = QrAuthMessage::LoginProtocols {
267 protocols: vec![LoginProtocolType::DeviceAuthorizationGrant],
268 homeserver: self.client.homeserver(),
269 };
270 channel.send_json(message).await?;
271
272 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#[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 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 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 self.state.set(GrantLoginProgress::EstablishingSecureChannel(
335 GeneratedQrProgress::QrReady(channel.qr_code_data().clone()),
336 ));
337
338 let channel = channel.connect().await?;
342
343 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 let mut channel = channel.confirm(check_code)?;
356
357 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 _rendezvous_server: MockedRendezvousServer,
416 device_authorization_grant: Option<AuthorizationGrant>,
417 secrets_bundle: Option<SecretsBundle>,
418 ) {
419 let qr_code_data = qr_code_rx.await.expect("Bob should receive the QR code");
421
422 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 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 let message = QrAuthMessage::LoginSuccess;
440 bob.send_json(message).await.unwrap();
441 return;
442 }
443 BobBehaviour::DeviceAlreadyExists => {
444 server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
447
448 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 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; }
467 _ => {
468 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 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 let message = QrAuthMessage::LoginSuccess;
493 bob.send_json(message).await.unwrap();
494
495 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; }
506 _ => {
507 server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
510
511 let message = QrAuthMessage::LoginSuccess;
513 bob.send_json(message).await.unwrap();
514 }
515 }
516
517 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 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 _rendezvous_server: MockedRendezvousServer,
541 homeserver: Url,
542 device_authorization_grant: Option<AuthorizationGrant>,
543 secrets_bundle: Option<SecretsBundle>,
544 ) {
545 let channel =
547 channel.connect().await.expect("Bob should be able to connect the secure channel");
548
549 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 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 let message = QrAuthMessage::LoginSuccess;
570 bob.send_json(message).await.unwrap();
571 return;
572 }
573 BobBehaviour::DeviceAlreadyExists => {
574 server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
577
578 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 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; }
597 _ => {
598 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 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 let message = QrAuthMessage::LoginSuccess;
623 bob.send_json(message).await.unwrap();
624
625 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; }
636 _ => {
637 server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
640
641 let message = QrAuthMessage::LoginSuccess;
643 bob.send_json(message).await.unwrap();
644 }
645 }
646
647 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 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 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 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 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_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 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 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 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 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 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_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 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 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 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 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 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_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 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 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 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 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_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 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 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 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 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 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_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 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 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 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 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_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 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 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 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 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 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_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 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 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 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 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_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 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 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 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 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_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 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 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 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 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 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 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 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 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 assert_matches!(grant.await, Err(QRCodeGrantLoginError::NotFound));
1926 updates_task.await.expect("Alice should run through all progress states");
1927 }
1928}