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, QrCodeIntent},
24 },
25};
26use oauth2::VerificationUriComplete;
27use ruma::time::Instant;
28use url::Url;
29#[cfg(doc)]
30use vodozemac::ecies::CheckCode;
31
32use super::{
33 LoginProtocolType, QrAuthMessage,
34 secure_channel::{EstablishedSecureChannel, SecureChannel},
35};
36use crate::{
37 Client,
38 authentication::oauth::qrcode::{
39 CheckCodeSender, GeneratedQrProgress, LoginFailureReason, QRCodeGrantLoginError,
40 QrProgress, SecureChannelError,
41 },
42};
43
44async fn export_secrets_bundle(client: &Client) -> Result<SecretsBundle, QRCodeGrantLoginError> {
45 let secrets_bundle = client
46 .olm_machine()
47 .await
48 .as_ref()
49 .ok_or_else(|| QRCodeGrantLoginError::MissingSecretsBackup(None))?
50 .store()
51 .export_secrets_bundle()
52 .await?;
53 Ok(secrets_bundle)
54}
55
56async fn finish_login_grant<Q>(
57 client: &Client,
58 channel: &mut EstablishedSecureChannel,
59 device_creation_timeout: Duration,
60 secrets_bundle: &SecretsBundle,
61 state: &SharedObservable<GrantLoginProgress<Q>>,
62) -> Result<(), QRCodeGrantLoginError> {
63 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 secrets_bundle = export_secrets_bundle(self.client).await?;
247
248 let mut channel = EstablishedSecureChannel::from_qr_code(
249 self.client.inner.http_client.inner.clone(),
250 self.qr_code_data,
251 QrCodeIntent::Reciprocate,
252 )
253 .await?;
254
255 let check_code = channel.check_code().to_owned();
259 self.state
260 .set(GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }));
261
262 let message = QrAuthMessage::LoginProtocols {
270 protocols: vec![LoginProtocolType::DeviceAuthorizationGrant],
271 homeserver: self.client.homeserver(),
272 };
273 channel.send_json(message).await?;
274
275 finish_login_grant(
278 self.client,
279 &mut channel,
280 self.device_creation_timeout,
281 &secrets_bundle,
282 &self.state,
283 )
284 .await
285 })
286 }
287}
288
289#[derive(Debug)]
292pub struct GrantLoginWithGeneratedQrCode<'a> {
293 client: &'a Client,
294 device_creation_timeout: Duration,
295 state: SharedObservable<GrantLoginProgress<GeneratedQrProgress>>,
296}
297
298impl<'a> GrantLoginWithGeneratedQrCode<'a> {
299 pub(crate) fn new(
300 client: &'a Client,
301 device_creation_timeout: Duration,
302 ) -> GrantLoginWithGeneratedQrCode<'a> {
303 GrantLoginWithGeneratedQrCode { client, device_creation_timeout, state: Default::default() }
304 }
305}
306
307impl GrantLoginWithGeneratedQrCode<'_> {
308 pub fn subscribe_to_progress(
315 &self,
316 ) -> impl Stream<Item = GrantLoginProgress<GeneratedQrProgress>> + use<> {
317 self.state.subscribe()
318 }
319}
320
321impl<'a> IntoFuture for GrantLoginWithGeneratedQrCode<'a> {
322 type Output = Result<(), QRCodeGrantLoginError>;
323 boxed_into_future!(extra_bounds: 'a);
324
325 fn into_future(self) -> Self::IntoFuture {
326 Box::pin(async move {
327 let homeserver_url = self.client.homeserver();
331 let http_client = self.client.inner.http_client.clone();
332 let secrets_bundle = export_secrets_bundle(self.client).await?;
333 let channel = SecureChannel::reciprocate(http_client, &homeserver_url).await?;
334
335 self.state.set(GrantLoginProgress::EstablishingSecureChannel(
339 GeneratedQrProgress::QrReady(channel.qr_code_data().clone()),
340 ));
341
342 let channel = channel.connect().await?;
346
347 let (tx, rx) = tokio::sync::oneshot::channel();
352 self.state.set(GrantLoginProgress::EstablishingSecureChannel(
353 GeneratedQrProgress::QrScanned(CheckCodeSender::new(tx)),
354 ));
355 let check_code = rx.await.map_err(|_| SecureChannelError::CannotReceiveCheckCode)?;
356
357 let mut channel = channel.confirm(check_code)?;
360
361 finish_login_grant(
369 self.client,
370 &mut channel,
371 self.device_creation_timeout,
372 &secrets_bundle,
373 &self.state,
374 )
375 .await
376 })
377 }
378}
379
380#[cfg(all(test, not(target_family = "wasm")))]
381mod test {
382 use assert_matches2::{assert_let, assert_matches};
383 use futures_util::StreamExt;
384 use matrix_sdk_base::crypto::types::SecretsBundle;
385 use matrix_sdk_common::executor::spawn;
386 use matrix_sdk_test::async_test;
387 use oauth2::{EndUserVerificationUrl, VerificationUriComplete};
388 use ruma::{owned_device_id, owned_user_id};
389 use tokio::sync::oneshot;
390 use tracing::debug;
391
392 use super::*;
393 use crate::{
394 authentication::oauth::qrcode::{
395 LoginFailureReason, QrAuthMessage,
396 messages::{AuthorizationGrant, LoginProtocolType},
397 secure_channel::{EstablishedSecureChannel, test::MockedRendezvousServer},
398 },
399 http_client::HttpClient,
400 test_utils::mocks::MatrixMockServer,
401 };
402
403 enum BobBehaviour {
404 HappyPath,
405 UnexpectedMessageInsteadOfLoginProtocol,
406 DeviceAlreadyExists,
407 DeviceNotCreated,
408 }
409
410 #[allow(clippy::too_many_arguments)]
411 async fn request_login_with_scanned_qr_code(
412 behaviour: BobBehaviour,
413 qr_code_rx: oneshot::Receiver<QrCodeData>,
414 check_code_tx: oneshot::Sender<u8>,
415 server: MatrixMockServer,
416 _rendezvous_server: MockedRendezvousServer,
420 device_authorization_grant: Option<AuthorizationGrant>,
421 secrets_bundle: Option<SecretsBundle>,
422 ) {
423 let qr_code_data = qr_code_rx.await.expect("Bob should receive the QR code");
425
426 let mut bob = EstablishedSecureChannel::from_qr_code(
428 reqwest::Client::new(),
429 &qr_code_data,
430 QrCodeIntent::Login,
431 )
432 .await
433 .expect("Bob should be able to connect the secure channel");
434
435 check_code_tx
437 .send(bob.check_code().to_digit())
438 .expect("Bob should be able to send the checkcode");
439
440 match behaviour {
441 BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol => {
442 let message = QrAuthMessage::LoginSuccess;
444 bob.send_json(message).await.unwrap();
445 return;
446 }
447 BobBehaviour::DeviceAlreadyExists => {
448 server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
451
452 let message = QrAuthMessage::LoginProtocol {
454 protocol: LoginProtocolType::DeviceAuthorizationGrant,
455 device_authorization_grant: device_authorization_grant
456 .expect("Bob needs the device authorization grant"),
457 device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
458 };
459 bob.send_json(message).await.unwrap();
460
461 let message = bob
463 .receive_json()
464 .await
465 .expect("Bob should receive the LoginFailure message from Alice");
466 assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
467 assert_matches!(reason, LoginFailureReason::DeviceAlreadyExists);
468
469 return; }
471 _ => {
472 let message = QrAuthMessage::LoginProtocol {
474 protocol: LoginProtocolType::DeviceAuthorizationGrant,
475 device_authorization_grant: device_authorization_grant
476 .expect("Bob needs the device authorization grant"),
477 device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
478 };
479 bob.send_json(message).await.unwrap();
480 }
481 }
482
483 let message = bob
485 .receive_json()
486 .await
487 .expect("Bob should receive the LoginProtocolAccepted message from Alice");
488 assert_let!(QrAuthMessage::LoginProtocolAccepted = message);
489
490 match behaviour {
491 BobBehaviour::DeviceNotCreated => {
492 let message = QrAuthMessage::LoginSuccess;
497 bob.send_json(message).await.unwrap();
498
499 let message = bob
502 .receive_json()
503 .await
504 .expect("Bob should receive the LoginFailure message from Alice");
505 assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
506 assert_matches!(reason, LoginFailureReason::DeviceNotFound);
507
508 return; }
510 _ => {
511 server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
514
515 let message = QrAuthMessage::LoginSuccess;
517 bob.send_json(message).await.unwrap();
518 }
519 }
520
521 let message = bob
523 .receive_json()
524 .await
525 .expect("Bob should receive the LoginSecrets message from Alice");
526 assert_let!(QrAuthMessage::LoginSecrets(bundle) = message);
527
528 assert_eq!(
530 serde_json::to_value(&secrets_bundle).unwrap(),
531 serde_json::to_value(&bundle).unwrap()
532 );
533 }
534
535 #[allow(clippy::too_many_arguments)]
536 async fn request_login_with_generated_qr_code(
537 behaviour: BobBehaviour,
538 channel: SecureChannel,
539 check_code_rx: oneshot::Receiver<u8>,
540 server: MatrixMockServer,
541 _rendezvous_server: MockedRendezvousServer,
545 homeserver: Url,
546 device_authorization_grant: Option<AuthorizationGrant>,
547 secrets_bundle: Option<SecretsBundle>,
548 ) {
549 let channel =
551 channel.connect().await.expect("Bob should be able to connect the secure channel");
552
553 let check_code = check_code_rx.await.expect("Bob should receive the checkcode");
555 let mut bob = channel
556 .confirm(check_code)
557 .expect("Bob should be able to confirm the channel is secure");
558
559 let message = bob
561 .receive_json()
562 .await
563 .expect("Bob should receive the LoginProtocolAccepted message from Alice");
564 assert_let!(
565 QrAuthMessage::LoginProtocols { protocols, homeserver: alice_homeserver } = message
566 );
567 assert_eq!(protocols, vec![LoginProtocolType::DeviceAuthorizationGrant]);
568 assert_eq!(alice_homeserver, homeserver);
569
570 match behaviour {
571 BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol => {
572 let message = QrAuthMessage::LoginSuccess;
574 bob.send_json(message).await.unwrap();
575 return;
576 }
577 BobBehaviour::DeviceAlreadyExists => {
578 server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
581
582 let message = QrAuthMessage::LoginProtocol {
584 protocol: LoginProtocolType::DeviceAuthorizationGrant,
585 device_authorization_grant: device_authorization_grant
586 .expect("Bob needs the device authorization grant"),
587 device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
588 };
589 bob.send_json(message).await.unwrap();
590
591 let message = bob
593 .receive_json()
594 .await
595 .expect("Bob should receive the LoginFailure message from Alice");
596 assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
597 assert_matches!(reason, LoginFailureReason::DeviceAlreadyExists);
598
599 return; }
601 _ => {
602 let message = QrAuthMessage::LoginProtocol {
604 protocol: LoginProtocolType::DeviceAuthorizationGrant,
605 device_authorization_grant: device_authorization_grant
606 .expect("Bob needs the device authorization grant"),
607 device_id: "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4".to_owned(),
608 };
609 bob.send_json(message).await.unwrap();
610 }
611 }
612
613 let message = bob
615 .receive_json()
616 .await
617 .expect("Bob should receive the LoginProtocolAccepted message from Alice");
618 assert_let!(QrAuthMessage::LoginProtocolAccepted = message);
619
620 match behaviour {
621 BobBehaviour::DeviceNotCreated => {
622 let message = QrAuthMessage::LoginSuccess;
627 bob.send_json(message).await.unwrap();
628
629 let message = bob
632 .receive_json()
633 .await
634 .expect("Bob should receive the LoginFailure message from Alice");
635 assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
636 assert_matches!(reason, LoginFailureReason::DeviceNotFound);
637
638 return; }
640 _ => {
641 server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
644
645 let message = QrAuthMessage::LoginSuccess;
647 bob.send_json(message).await.unwrap();
648 }
649 }
650
651 let message = bob
653 .receive_json()
654 .await
655 .expect("Bob should receive the LoginSecrets message from Alice");
656 assert_let!(QrAuthMessage::LoginSecrets(bundle) = message);
657
658 assert_eq!(
660 serde_json::to_value(&secrets_bundle).unwrap(),
661 serde_json::to_value(&bundle).unwrap()
662 );
663 }
664
665 #[async_test]
666 async fn test_grant_login_with_generated_qr_code() {
667 let server = MatrixMockServer::new().await;
668 let rendezvous_server =
669 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
670 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
671
672 let device_authorization_grant = AuthorizationGrant {
673 verification_uri_complete: Some(VerificationUriComplete::new(
674 "https://id.matrix.org/device/abcde".to_owned(),
675 )),
676 verification_uri: EndUserVerificationUrl::new(
677 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
678 )
679 .unwrap(),
680 };
681
682 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
683 server
684 .mock_upload_cross_signing_keys()
685 .ok()
686 .expect(1)
687 .named("upload_xsigning_keys")
688 .mount()
689 .await;
690 server
691 .mock_upload_cross_signing_signatures()
692 .ok()
693 .expect(1)
694 .named("upload_xsigning_signatures")
695 .mount()
696 .await;
697
698 let user_id = owned_user_id!("@alice:example.org");
700 let device_id = owned_device_id!("ALICE_DEVICE");
701 let alice = server
702 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
703 .logged_in_with_oauth()
704 .build()
705 .await;
706 alice
707 .encryption()
708 .bootstrap_cross_signing(None)
709 .await
710 .expect("Alice should be able to set up cross signing");
711
712 let oauth = alice.oauth();
714 let grant = oauth
715 .grant_login_with_qr_code()
716 .device_creation_timeout(Duration::from_secs(2))
717 .generate();
718 let secrets_bundle = export_secrets_bundle(&alice)
719 .await
720 .expect("Alice should be able to export the secrets bundle");
721 let (qr_code_tx, qr_code_rx) = oneshot::channel();
722 let (checkcode_tx, checkcode_rx) = oneshot::channel();
723
724 let mut updates = grant.subscribe_to_progress();
726 let mut state = grant.state.get();
727 let verification_uri_complete =
728 device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
729 assert_matches!(state.clone(), GrantLoginProgress::Starting);
730 let updates_task = spawn(async move {
731 let mut qr_code_tx = Some(qr_code_tx);
732 let mut checkcode_rx = Some(checkcode_rx);
733
734 while let Some(update) = updates.next().await {
735 match &update {
736 GrantLoginProgress::Starting => {
737 assert_matches!(state, GrantLoginProgress::Starting);
738 }
739 GrantLoginProgress::EstablishingSecureChannel(
740 GeneratedQrProgress::QrReady(qr_code_data),
741 ) => {
742 assert_matches!(state, GrantLoginProgress::Starting);
743 qr_code_tx
744 .take()
745 .expect("The QR code should only be forwarded once")
746 .send(qr_code_data.clone())
747 .expect("Alice should be able to forward the QR code");
748 }
749 GrantLoginProgress::EstablishingSecureChannel(
750 GeneratedQrProgress::QrScanned(checkcode_sender),
751 ) => {
752 assert_matches!(
753 state,
754 GrantLoginProgress::EstablishingSecureChannel(
755 GeneratedQrProgress::QrReady(_)
756 )
757 );
758 let checkcode = checkcode_rx
759 .take()
760 .expect("The checkcode should only be forwarded once")
761 .await
762 .expect("Alice should receive the checkcode");
763 checkcode_sender
764 .send(checkcode)
765 .await
766 .expect("Alice should be able to forward the checkcode");
767 }
768 GrantLoginProgress::WaitingForAuth { verification_uri } => {
769 assert_matches!(
770 state,
771 GrantLoginProgress::EstablishingSecureChannel(
772 GeneratedQrProgress::QrScanned(_)
773 )
774 );
775 assert_eq!(verification_uri.as_str(), verification_uri_complete);
776 }
777 GrantLoginProgress::SyncingSecrets => {
778 assert_matches!(state, GrantLoginProgress::WaitingForAuth { .. });
779 }
780 GrantLoginProgress::Done => {
781 assert_matches!(state, GrantLoginProgress::SyncingSecrets);
782 break;
783 }
784 }
785 state = update;
786 }
787 });
788
789 let bob_task = spawn(async move {
791 request_login_with_scanned_qr_code(
792 BobBehaviour::HappyPath,
793 qr_code_rx,
794 checkcode_tx,
795 server,
796 rendezvous_server,
797 Some(device_authorization_grant),
798 Some(secrets_bundle),
799 )
800 .await;
801 });
802
803 grant.await.expect("Alice should be able to grant the login");
805 updates_task.await.expect("Alice should run through all progress states");
806 bob_task.await.expect("Bob's task should finish");
807 }
808
809 #[async_test]
810 async fn test_grant_login_with_scanned_qr_code() {
811 let server = MatrixMockServer::new().await;
812 let rendezvous_server =
813 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
814 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
815
816 let device_authorization_grant = AuthorizationGrant {
817 verification_uri_complete: Some(VerificationUriComplete::new(
818 "https://id.matrix.org/device/abcde".to_owned(),
819 )),
820 verification_uri: EndUserVerificationUrl::new(
821 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
822 )
823 .unwrap(),
824 };
825
826 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
827 server
828 .mock_upload_cross_signing_keys()
829 .ok()
830 .expect(1)
831 .named("upload_xsigning_keys")
832 .mount()
833 .await;
834 server
835 .mock_upload_cross_signing_signatures()
836 .ok()
837 .expect(1)
838 .named("upload_xsigning_signatures")
839 .mount()
840 .await;
841
842 let client = HttpClient::new(reqwest::Client::new(), Default::default());
844 let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
845 .await
846 .expect("Bob should be able to create a secure channel.");
847 let qr_code_data = channel.qr_code_data().clone();
848
849 let user_id = owned_user_id!("@alice:example.org");
851 let device_id = owned_device_id!("ALICE_DEVICE");
852 let alice = server
853 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
854 .logged_in_with_oauth()
855 .build()
856 .await;
857 alice
858 .encryption()
859 .bootstrap_cross_signing(None)
860 .await
861 .expect("Alice should be able to set up cross signing");
862
863 let oauth = alice.oauth();
865 let grant = oauth
866 .grant_login_with_qr_code()
867 .device_creation_timeout(Duration::from_secs(2))
868 .scan(&qr_code_data);
869 let secrets_bundle = export_secrets_bundle(&alice)
870 .await
871 .expect("Alice should be able to export the secrets bundle");
872 let (checkcode_tx, checkcode_rx) = oneshot::channel();
873
874 let mut updates = grant.subscribe_to_progress();
876 let mut state = grant.state.get();
877 let verification_uri_complete =
878 device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
879 assert_matches!(state.clone(), GrantLoginProgress::Starting);
880 let updates_task = spawn(async move {
881 let mut checkcode_tx = Some(checkcode_tx);
882
883 while let Some(update) = updates.next().await {
884 match &update {
885 GrantLoginProgress::Starting => {
886 assert_matches!(state, GrantLoginProgress::Starting);
887 }
888 GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
889 assert_matches!(state, GrantLoginProgress::Starting);
890 checkcode_tx
891 .take()
892 .expect("The checkcode should only be forwarded once")
893 .send(check_code.to_digit())
894 .expect("Alice should be able to forward the checkcode");
895 }
896 GrantLoginProgress::WaitingForAuth { verification_uri } => {
897 assert_matches!(
898 state,
899 GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
900 );
901 assert_eq!(verification_uri.as_str(), verification_uri_complete);
902 }
903 GrantLoginProgress::SyncingSecrets => {
904 assert_matches!(state, GrantLoginProgress::WaitingForAuth { .. });
905 }
906 GrantLoginProgress::Done => {
907 assert_matches!(state, GrantLoginProgress::SyncingSecrets);
908 break;
909 }
910 }
911 state = update;
912 }
913 });
914
915 let bob_task = spawn(async move {
917 request_login_with_generated_qr_code(
918 BobBehaviour::HappyPath,
919 channel,
920 checkcode_rx,
921 server,
922 rendezvous_server,
923 alice.homeserver(),
924 Some(device_authorization_grant),
925 Some(secrets_bundle),
926 )
927 .await;
928 });
929
930 grant.await.expect("Alice should be able to grant the login");
932 updates_task.await.expect("Alice should run through all progress states");
933 bob_task.await.expect("Bob's task should finish");
934 }
935
936 #[async_test]
937 async fn test_grant_login_with_scanned_qr_code_with_homeserver_swap() {
938 let server = MatrixMockServer::new().await;
939 let rendezvous_server =
940 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
941 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
942
943 let device_authorization_grant = AuthorizationGrant {
944 verification_uri_complete: Some(VerificationUriComplete::new(
945 "https://id.matrix.org/device/abcde".to_owned(),
946 )),
947 verification_uri: EndUserVerificationUrl::new(
948 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
949 )
950 .unwrap(),
951 };
952
953 let login_server = MatrixMockServer::new().await;
954
955 login_server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
956 login_server
957 .mock_upload_cross_signing_keys()
958 .ok()
959 .expect(1)
960 .named("upload_xsigning_keys")
961 .mount()
962 .await;
963 login_server
964 .mock_upload_cross_signing_signatures()
965 .ok()
966 .expect(1)
967 .named("upload_xsigning_signatures")
968 .mount()
969 .await;
970
971 let client = HttpClient::new(reqwest::Client::new(), Default::default());
973 let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
974 .await
975 .expect("Bob should be able to create a secure channel.");
976 let qr_code_data = channel.qr_code_data().clone();
977
978 let user_id = owned_user_id!("@alice:example.org");
980 let device_id = owned_device_id!("ALICE_DEVICE");
981 let alice = login_server
982 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
983 .logged_in_with_oauth()
984 .build()
985 .await;
986 alice
987 .encryption()
988 .bootstrap_cross_signing(None)
989 .await
990 .expect("Alice should be able to set up cross signing");
991
992 let oauth = alice.oauth();
994 let grant = oauth
995 .grant_login_with_qr_code()
996 .device_creation_timeout(Duration::from_secs(2))
997 .scan(&qr_code_data);
998 let secrets_bundle = export_secrets_bundle(&alice)
999 .await
1000 .expect("Alice should be able to export the secrets bundle");
1001 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1002
1003 let mut updates = grant.subscribe_to_progress();
1005 let mut state = grant.state.get();
1006 let verification_uri_complete =
1007 device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
1008 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1009 let updates_task = spawn(async move {
1010 let mut checkcode_tx = Some(checkcode_tx);
1011
1012 while let Some(update) = updates.next().await {
1013 match &update {
1014 GrantLoginProgress::Starting => {
1015 assert_matches!(state, GrantLoginProgress::Starting);
1016 }
1017 GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1018 assert_matches!(state, GrantLoginProgress::Starting);
1019 checkcode_tx
1020 .take()
1021 .expect("The checkcode should only be forwarded once")
1022 .send(check_code.to_digit())
1023 .expect("Alice should be able to forward the checkcode");
1024 }
1025 GrantLoginProgress::WaitingForAuth { verification_uri } => {
1026 assert_matches!(
1027 state,
1028 GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
1029 );
1030 assert_eq!(verification_uri.as_str(), verification_uri_complete);
1031 }
1032 GrantLoginProgress::SyncingSecrets => {
1033 assert_matches!(state, GrantLoginProgress::WaitingForAuth { .. });
1034 }
1035 GrantLoginProgress::Done => {
1036 assert_matches!(state, GrantLoginProgress::SyncingSecrets);
1037 break;
1038 }
1039 }
1040 state = update;
1041 }
1042 });
1043
1044 let bob_task = spawn(async move {
1046 request_login_with_generated_qr_code(
1047 BobBehaviour::HappyPath,
1048 channel,
1049 checkcode_rx,
1050 login_server,
1051 rendezvous_server,
1052 alice.homeserver(),
1053 Some(device_authorization_grant),
1054 Some(secrets_bundle),
1055 )
1056 .await;
1057 });
1058
1059 grant.await.expect("Alice should be able to grant the login");
1061 updates_task.await.expect("Alice should run through all progress states");
1062 bob_task.await.expect("Bob's task should finish");
1063 }
1064
1065 #[async_test]
1066 async fn test_grant_login_with_generated_qr_code_unexpected_message_instead_of_login_protocol()
1067 {
1068 let server = MatrixMockServer::new().await;
1069 let rendezvous_server =
1070 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1071 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1072
1073 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1074 server
1075 .mock_upload_cross_signing_keys()
1076 .ok()
1077 .expect(1)
1078 .named("upload_xsigning_keys")
1079 .mount()
1080 .await;
1081 server
1082 .mock_upload_cross_signing_signatures()
1083 .ok()
1084 .expect(1)
1085 .named("upload_xsigning_signatures")
1086 .mount()
1087 .await;
1088
1089 let user_id = owned_user_id!("@alice:example.org");
1091 let device_id = owned_device_id!("ALICE_DEVICE");
1092 let alice = server
1093 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1094 .logged_in_with_oauth()
1095 .build()
1096 .await;
1097 alice
1098 .encryption()
1099 .bootstrap_cross_signing(None)
1100 .await
1101 .expect("Alice should be able to set up cross signing");
1102
1103 let oauth = alice.oauth();
1105 let grant = oauth
1106 .grant_login_with_qr_code()
1107 .device_creation_timeout(Duration::from_secs(2))
1108 .generate();
1109 let (qr_code_tx, qr_code_rx) = oneshot::channel();
1110 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1111
1112 let mut updates = grant.subscribe_to_progress();
1114 let mut state = grant.state.get();
1115 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1116 let updates_task = spawn(async move {
1117 let mut qr_code_tx = Some(qr_code_tx);
1118 let mut checkcode_rx = Some(checkcode_rx);
1119
1120 while let Some(update) = updates.next().await {
1121 match &update {
1122 GrantLoginProgress::Starting => {
1123 assert_matches!(state, GrantLoginProgress::Starting);
1124 }
1125 GrantLoginProgress::EstablishingSecureChannel(
1126 GeneratedQrProgress::QrReady(qr_code_data),
1127 ) => {
1128 assert_matches!(state, GrantLoginProgress::Starting);
1129 qr_code_tx
1130 .take()
1131 .expect("The QR code should only be forwarded once")
1132 .send(qr_code_data.clone())
1133 .expect("Alice should be able to forward the QR code");
1134 }
1135 GrantLoginProgress::EstablishingSecureChannel(
1136 GeneratedQrProgress::QrScanned(checkcode_sender),
1137 ) => {
1138 assert_matches!(
1139 state,
1140 GrantLoginProgress::EstablishingSecureChannel(
1141 GeneratedQrProgress::QrReady(_)
1142 )
1143 );
1144 let checkcode = checkcode_rx
1145 .take()
1146 .expect("The checkcode should only be forwarded once")
1147 .await
1148 .expect("Alice should receive the checkcode");
1149 checkcode_sender
1150 .send(checkcode)
1151 .await
1152 .expect("Alice should be able to forward the checkcode");
1153 break;
1154 }
1155 _ => {
1156 panic!("Alice should abort the process");
1157 }
1158 }
1159 state = update;
1160 }
1161 });
1162
1163 let bob_task = spawn(async move {
1165 request_login_with_scanned_qr_code(
1166 BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol,
1167 qr_code_rx,
1168 checkcode_tx,
1169 server,
1170 rendezvous_server,
1171 None,
1172 None,
1173 )
1174 .await;
1175 });
1176
1177 grant.await.expect_err("Alice should abort the login");
1179 updates_task.await.expect("Alice should run through all progress states");
1180 bob_task.await.expect("Bob's task should finish");
1181 }
1182
1183 #[async_test]
1184 async fn test_grant_login_with_scanned_qr_code_unexpected_message_instead_of_login_protocol() {
1185 let server = MatrixMockServer::new().await;
1186 let rendezvous_server =
1187 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1188 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1189
1190 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1191 server
1192 .mock_upload_cross_signing_keys()
1193 .ok()
1194 .expect(1)
1195 .named("upload_xsigning_keys")
1196 .mount()
1197 .await;
1198 server
1199 .mock_upload_cross_signing_signatures()
1200 .ok()
1201 .expect(1)
1202 .named("upload_xsigning_signatures")
1203 .mount()
1204 .await;
1205
1206 let client = HttpClient::new(reqwest::Client::new(), Default::default());
1208 let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1209 .await
1210 .expect("Bob should be able to create a secure channel.");
1211 let qr_code_data = channel.qr_code_data().clone();
1212
1213 let user_id = owned_user_id!("@alice:example.org");
1215 let device_id = owned_device_id!("ALICE_DEVICE");
1216 let alice = server
1217 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1218 .logged_in_with_oauth()
1219 .build()
1220 .await;
1221 alice
1222 .encryption()
1223 .bootstrap_cross_signing(None)
1224 .await
1225 .expect("Alice should be able to set up cross signing");
1226
1227 let oauth = alice.oauth();
1229 let grant = oauth
1230 .grant_login_with_qr_code()
1231 .device_creation_timeout(Duration::from_secs(2))
1232 .scan(&qr_code_data);
1233 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1234
1235 let mut updates = grant.subscribe_to_progress();
1237 let mut state = grant.state.get();
1238 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1239 let updates_task = spawn(async move {
1240 let mut checkcode_tx = Some(checkcode_tx);
1241
1242 while let Some(update) = updates.next().await {
1243 match &update {
1244 GrantLoginProgress::Starting => {
1245 assert_matches!(state, GrantLoginProgress::Starting);
1246 }
1247 GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1248 assert_matches!(state, GrantLoginProgress::Starting);
1249 checkcode_tx
1250 .take()
1251 .expect("The checkcode should only be forwarded once")
1252 .send(check_code.to_digit())
1253 .expect("Alice should be able to forward the checkcode");
1254 break;
1255 }
1256 _ => {
1257 panic!("Alice should abort the process");
1258 }
1259 }
1260 state = update;
1261 }
1262 });
1263
1264 let bob_task = spawn(async move {
1266 request_login_with_generated_qr_code(
1267 BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol,
1268 channel,
1269 checkcode_rx,
1270 server,
1271 rendezvous_server,
1272 alice.homeserver(),
1273 None,
1274 None,
1275 )
1276 .await;
1277 });
1278
1279 grant.await.expect_err("Alice should abort the login");
1280
1281 updates_task.await.expect("Alice should run through all progress states");
1283 bob_task.await.expect("Bob's task should finish");
1284 }
1285
1286 #[async_test]
1287 async fn test_grant_login_with_generated_qr_code_device_already_exists() {
1288 let server = MatrixMockServer::new().await;
1289 let rendezvous_server =
1290 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1291 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1292
1293 let device_authorization_grant = AuthorizationGrant {
1294 verification_uri_complete: Some(VerificationUriComplete::new(
1295 "https://id.matrix.org/device/abcde".to_owned(),
1296 )),
1297 verification_uri: EndUserVerificationUrl::new(
1298 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1299 )
1300 .unwrap(),
1301 };
1302
1303 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1304 server
1305 .mock_upload_cross_signing_keys()
1306 .ok()
1307 .expect(1)
1308 .named("upload_xsigning_keys")
1309 .mount()
1310 .await;
1311 server
1312 .mock_upload_cross_signing_signatures()
1313 .ok()
1314 .expect(1)
1315 .named("upload_xsigning_signatures")
1316 .mount()
1317 .await;
1318
1319 let user_id = owned_user_id!("@alice:example.org");
1321 let device_id = owned_device_id!("ALICE_DEVICE");
1322 let alice = server
1323 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1324 .logged_in_with_oauth()
1325 .build()
1326 .await;
1327 alice
1328 .encryption()
1329 .bootstrap_cross_signing(None)
1330 .await
1331 .expect("Alice should be able to set up cross signing");
1332
1333 let oauth = alice.oauth();
1335 let grant = oauth
1336 .grant_login_with_qr_code()
1337 .device_creation_timeout(Duration::from_secs(2))
1338 .generate();
1339 let (qr_code_tx, qr_code_rx) = oneshot::channel();
1340 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1341
1342 let mut updates = grant.subscribe_to_progress();
1344 let mut state = grant.state.get();
1345 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1346 let updates_task = spawn(async move {
1347 let mut qr_code_tx = Some(qr_code_tx);
1348 let mut checkcode_rx = Some(checkcode_rx);
1349
1350 while let Some(update) = updates.next().await {
1351 match &update {
1352 GrantLoginProgress::Starting => {
1353 assert_matches!(state, GrantLoginProgress::Starting);
1354 }
1355 GrantLoginProgress::EstablishingSecureChannel(
1356 GeneratedQrProgress::QrReady(qr_code_data),
1357 ) => {
1358 assert_matches!(state, GrantLoginProgress::Starting);
1359 qr_code_tx
1360 .take()
1361 .expect("The QR code should only be forwarded once")
1362 .send(qr_code_data.clone())
1363 .expect("Alice should be able to forward the QR code");
1364 }
1365 GrantLoginProgress::EstablishingSecureChannel(
1366 GeneratedQrProgress::QrScanned(checkcode_sender),
1367 ) => {
1368 assert_matches!(
1369 state,
1370 GrantLoginProgress::EstablishingSecureChannel(
1371 GeneratedQrProgress::QrReady(_)
1372 )
1373 );
1374 let checkcode = checkcode_rx
1375 .take()
1376 .expect("The checkcode should only be forwarded once")
1377 .await
1378 .expect("Alice should receive the checkcode");
1379 checkcode_sender
1380 .send(checkcode)
1381 .await
1382 .expect("Alice should be able to forward the checkcode");
1383 }
1384 _ => {
1385 panic!("Alice should abort the process");
1386 }
1387 }
1388 state = update;
1389 }
1390 });
1391
1392 let bob_task = spawn(async move {
1394 request_login_with_scanned_qr_code(
1395 BobBehaviour::DeviceAlreadyExists,
1396 qr_code_rx,
1397 checkcode_tx,
1398 server,
1399 rendezvous_server,
1400 Some(device_authorization_grant),
1401 None,
1402 )
1403 .await;
1404 });
1405
1406 grant.await.expect_err("Alice should abort the login");
1408 updates_task.await.expect("Alice should run through all progress states");
1409 bob_task.await.expect("Bob's task should finish");
1410 }
1411
1412 #[async_test]
1413 async fn test_grant_login_with_scanned_qr_code_device_already_exists() {
1414 let server = MatrixMockServer::new().await;
1415 let rendezvous_server =
1416 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1417 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1418
1419 let device_authorization_grant = AuthorizationGrant {
1420 verification_uri_complete: Some(VerificationUriComplete::new(
1421 "https://id.matrix.org/device/abcde".to_owned(),
1422 )),
1423 verification_uri: EndUserVerificationUrl::new(
1424 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1425 )
1426 .unwrap(),
1427 };
1428
1429 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1430 server
1431 .mock_upload_cross_signing_keys()
1432 .ok()
1433 .expect(1)
1434 .named("upload_xsigning_keys")
1435 .mount()
1436 .await;
1437 server
1438 .mock_upload_cross_signing_signatures()
1439 .ok()
1440 .expect(1)
1441 .named("upload_xsigning_signatures")
1442 .mount()
1443 .await;
1444
1445 let client = HttpClient::new(reqwest::Client::new(), Default::default());
1447 let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1448 .await
1449 .expect("Bob should be able to create a secure channel.");
1450 let qr_code_data = channel.qr_code_data().clone();
1451
1452 let user_id = owned_user_id!("@alice:example.org");
1454 let device_id = owned_device_id!("ALICE_DEVICE");
1455 let alice = server
1456 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1457 .logged_in_with_oauth()
1458 .build()
1459 .await;
1460 alice
1461 .encryption()
1462 .bootstrap_cross_signing(None)
1463 .await
1464 .expect("Alice should be able to set up cross signing");
1465
1466 let oauth = alice.oauth();
1468 let grant = oauth
1469 .grant_login_with_qr_code()
1470 .device_creation_timeout(Duration::from_secs(2))
1471 .scan(&qr_code_data);
1472 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1473
1474 let mut updates = grant.subscribe_to_progress();
1476 let mut state = grant.state.get();
1477 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1478 let updates_task = spawn(async move {
1479 let mut checkcode_tx = Some(checkcode_tx);
1480
1481 while let Some(update) = updates.next().await {
1482 match &update {
1483 GrantLoginProgress::Starting => {
1484 assert_matches!(state, GrantLoginProgress::Starting);
1485 }
1486 GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1487 assert_matches!(state, GrantLoginProgress::Starting);
1488 checkcode_tx
1489 .take()
1490 .expect("The checkcode should only be forwarded once")
1491 .send(check_code.to_digit())
1492 .expect("Alice should be able to forward the checkcode");
1493 }
1494 _ => {
1495 panic!("Alice should abort the process");
1496 }
1497 }
1498 state = update;
1499 }
1500 });
1501
1502 let bob_task = spawn(async move {
1504 request_login_with_generated_qr_code(
1505 BobBehaviour::DeviceAlreadyExists,
1506 channel,
1507 checkcode_rx,
1508 server,
1509 rendezvous_server,
1510 alice.homeserver(),
1511 Some(device_authorization_grant),
1512 None,
1513 )
1514 .await;
1515 });
1516
1517 grant.await.expect_err("Alice should abort the login");
1519 updates_task.await.expect("Alice should run through all progress states");
1520 bob_task.await.expect("Bob's task should finish");
1521 }
1522
1523 #[async_test]
1524 async fn test_grant_login_with_generated_qr_code_device_not_created() {
1525 let server = MatrixMockServer::new().await;
1526 let rendezvous_server =
1527 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1528 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1529
1530 let device_authorization_grant = AuthorizationGrant {
1531 verification_uri_complete: Some(VerificationUriComplete::new(
1532 "https://id.matrix.org/device/abcde".to_owned(),
1533 )),
1534 verification_uri: EndUserVerificationUrl::new(
1535 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1536 )
1537 .unwrap(),
1538 };
1539
1540 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1541 server
1542 .mock_upload_cross_signing_keys()
1543 .ok()
1544 .expect(1)
1545 .named("upload_xsigning_keys")
1546 .mount()
1547 .await;
1548 server
1549 .mock_upload_cross_signing_signatures()
1550 .ok()
1551 .expect(1)
1552 .named("upload_xsigning_signatures")
1553 .mount()
1554 .await;
1555
1556 let user_id = owned_user_id!("@alice:example.org");
1558 let device_id = owned_device_id!("ALICE_DEVICE");
1559 let alice = server
1560 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1561 .logged_in_with_oauth()
1562 .build()
1563 .await;
1564 alice
1565 .encryption()
1566 .bootstrap_cross_signing(None)
1567 .await
1568 .expect("Alice should be able to set up cross signing");
1569
1570 let oauth = alice.oauth();
1572 let grant = oauth
1573 .grant_login_with_qr_code()
1574 .device_creation_timeout(Duration::from_secs(2))
1575 .generate();
1576 let (qr_code_tx, qr_code_rx) = oneshot::channel();
1577 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1578
1579 let mut updates = grant.subscribe_to_progress();
1581 let mut state = grant.state.get();
1582 let verification_uri_complete =
1583 device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
1584 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1585 let updates_task = spawn(async move {
1586 let mut qr_code_tx = Some(qr_code_tx);
1587 let mut checkcode_rx = Some(checkcode_rx);
1588
1589 while let Some(update) = updates.next().await {
1590 match &update {
1591 GrantLoginProgress::Starting => {
1592 assert_matches!(state, GrantLoginProgress::Starting);
1593 }
1594 GrantLoginProgress::EstablishingSecureChannel(
1595 GeneratedQrProgress::QrReady(qr_code_data),
1596 ) => {
1597 assert_matches!(state, GrantLoginProgress::Starting);
1598 qr_code_tx
1599 .take()
1600 .expect("The QR code should only be forwarded once")
1601 .send(qr_code_data.clone())
1602 .expect("Alice should be able to forward the QR code");
1603 }
1604 GrantLoginProgress::EstablishingSecureChannel(
1605 GeneratedQrProgress::QrScanned(checkcode_sender),
1606 ) => {
1607 assert_matches!(
1608 state,
1609 GrantLoginProgress::EstablishingSecureChannel(
1610 GeneratedQrProgress::QrReady(_)
1611 )
1612 );
1613 let checkcode = checkcode_rx
1614 .take()
1615 .expect("The checkcode should only be forwarded once")
1616 .await
1617 .expect("Alice should receive the checkcode");
1618 checkcode_sender
1619 .send(checkcode)
1620 .await
1621 .expect("Alice should be able to forward the checkcode");
1622 }
1623 GrantLoginProgress::WaitingForAuth { verification_uri } => {
1624 assert_matches!(
1625 state,
1626 GrantLoginProgress::EstablishingSecureChannel(
1627 GeneratedQrProgress::QrScanned(_)
1628 )
1629 );
1630 assert_eq!(verification_uri.as_str(), verification_uri_complete);
1631 }
1632 _ => {
1633 panic!("Alice should abort the process");
1634 }
1635 }
1636 state = update;
1637 }
1638 });
1639
1640 let bob_task = spawn(async move {
1642 request_login_with_scanned_qr_code(
1643 BobBehaviour::DeviceNotCreated,
1644 qr_code_rx,
1645 checkcode_tx,
1646 server,
1647 rendezvous_server,
1648 Some(device_authorization_grant),
1649 None,
1650 )
1651 .await;
1652 });
1653
1654 grant.await.expect_err("Alice should abort the login");
1655 updates_task.await.expect("Alice should run through all progress states");
1656 bob_task.await.expect("Bob's task should finish");
1657 }
1658
1659 #[async_test]
1660 async fn test_grant_login_with_scanned_qr_code_device_not_created() {
1661 let server = MatrixMockServer::new().await;
1662 let rendezvous_server =
1663 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1664 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1665
1666 let device_authorization_grant = AuthorizationGrant {
1667 verification_uri_complete: Some(VerificationUriComplete::new(
1668 "https://id.matrix.org/device/abcde".to_owned(),
1669 )),
1670 verification_uri: EndUserVerificationUrl::new(
1671 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1672 )
1673 .unwrap(),
1674 };
1675
1676 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1677 server
1678 .mock_upload_cross_signing_keys()
1679 .ok()
1680 .expect(1)
1681 .named("upload_xsigning_keys")
1682 .mount()
1683 .await;
1684 server
1685 .mock_upload_cross_signing_signatures()
1686 .ok()
1687 .expect(1)
1688 .named("upload_xsigning_signatures")
1689 .mount()
1690 .await;
1691
1692 let client = HttpClient::new(reqwest::Client::new(), Default::default());
1694 let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1695 .await
1696 .expect("Bob should be able to create a secure channel.");
1697 let qr_code_data = channel.qr_code_data().clone();
1698
1699 let user_id = owned_user_id!("@alice:example.org");
1701 let device_id = owned_device_id!("ALICE_DEVICE");
1702 let alice = server
1703 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1704 .logged_in_with_oauth()
1705 .build()
1706 .await;
1707 alice
1708 .encryption()
1709 .bootstrap_cross_signing(None)
1710 .await
1711 .expect("Alice should be able to set up cross signing");
1712
1713 let oauth = alice.oauth();
1715 let grant = oauth
1716 .grant_login_with_qr_code()
1717 .device_creation_timeout(Duration::from_secs(2))
1718 .scan(&qr_code_data);
1719 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1720
1721 let mut updates = grant.subscribe_to_progress();
1723 let mut state = grant.state.get();
1724 let verification_uri_complete =
1725 device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
1726 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1727 let updates_task = spawn(async move {
1728 let mut checkcode_tx = Some(checkcode_tx);
1729
1730 while let Some(update) = updates.next().await {
1731 match &update {
1732 GrantLoginProgress::Starting => {
1733 assert_matches!(state, GrantLoginProgress::Starting);
1734 }
1735 GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1736 assert_matches!(state, GrantLoginProgress::Starting);
1737 checkcode_tx
1738 .take()
1739 .expect("The checkcode should only be forwarded once")
1740 .send(check_code.to_digit())
1741 .expect("Alice should be able to forward the checkcode");
1742 }
1743 GrantLoginProgress::WaitingForAuth { verification_uri } => {
1744 assert_matches!(
1745 state,
1746 GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
1747 );
1748 assert_eq!(verification_uri.as_str(), verification_uri_complete);
1749 }
1750 _ => {
1751 panic!("Alice should abort the process");
1752 }
1753 }
1754 state = update;
1755 }
1756 });
1757
1758 let bob_task = spawn(async move {
1760 request_login_with_generated_qr_code(
1761 BobBehaviour::DeviceNotCreated,
1762 channel,
1763 checkcode_rx,
1764 server,
1765 rendezvous_server,
1766 alice.homeserver(),
1767 Some(device_authorization_grant),
1768 None,
1769 )
1770 .await;
1771 });
1772
1773 grant.await.expect_err("Alice should abort the login");
1774 updates_task.await.expect("Alice should run through all progress states");
1775 bob_task.await.expect("Bob's task should finish");
1776 }
1777
1778 #[async_test]
1779 async fn test_grant_login_with_generated_qr_code_session_expired() {
1780 let server = MatrixMockServer::new().await;
1781 let rendezvous_server =
1782 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::from_secs(2))
1783 .await;
1784 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1785
1786 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1787 server
1788 .mock_upload_cross_signing_keys()
1789 .ok()
1790 .expect(1)
1791 .named("upload_xsigning_keys")
1792 .mount()
1793 .await;
1794 server
1795 .mock_upload_cross_signing_signatures()
1796 .ok()
1797 .expect(1)
1798 .named("upload_xsigning_signatures")
1799 .mount()
1800 .await;
1801
1802 let user_id = owned_user_id!("@alice:example.org");
1804 let device_id = owned_device_id!("ALICE_DEVICE");
1805 let alice = server
1806 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1807 .logged_in_with_oauth()
1808 .build()
1809 .await;
1810 alice
1811 .encryption()
1812 .bootstrap_cross_signing(None)
1813 .await
1814 .expect("Alice should be able to set up cross signing");
1815
1816 let oauth = alice.oauth();
1818 let grant = oauth
1819 .grant_login_with_qr_code()
1820 .device_creation_timeout(Duration::from_secs(2))
1821 .generate();
1822
1823 let mut updates = grant.subscribe_to_progress();
1825 let mut state = grant.state.get();
1826 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1827 let updates_task = spawn(async move {
1828 while let Some(update) = updates.next().await {
1829 match &update {
1830 GrantLoginProgress::Starting => {
1831 assert_matches!(state, GrantLoginProgress::Starting);
1832 }
1833 GrantLoginProgress::EstablishingSecureChannel(
1834 GeneratedQrProgress::QrReady(_),
1835 ) => {
1836 assert_matches!(state, GrantLoginProgress::Starting);
1837 }
1838 _ => {
1839 panic!("Alice should abort the process");
1840 }
1841 }
1842 state = update;
1843 }
1844 });
1845
1846 assert_matches!(grant.await, Err(QRCodeGrantLoginError::NotFound));
1850 updates_task.await.expect("Alice should run through all progress states");
1851 }
1852
1853 #[async_test]
1854 async fn test_grant_login_with_scanned_qr_code_session_expired() {
1855 let server = MatrixMockServer::new().await;
1856 let rendezvous_server =
1857 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::from_secs(2))
1858 .await;
1859 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1860
1861 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1862 server
1863 .mock_upload_cross_signing_keys()
1864 .ok()
1865 .expect(1)
1866 .named("upload_xsigning_keys")
1867 .mount()
1868 .await;
1869 server
1870 .mock_upload_cross_signing_signatures()
1871 .ok()
1872 .expect(1)
1873 .named("upload_xsigning_signatures")
1874 .mount()
1875 .await;
1876
1877 let client = HttpClient::new(reqwest::Client::new(), Default::default());
1879 let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1880 .await
1881 .expect("Bob should be able to create a secure channel.");
1882 let qr_code_data = channel.qr_code_data().clone();
1883
1884 let user_id = owned_user_id!("@alice:example.org");
1886 let device_id = owned_device_id!("ALICE_DEVICE");
1887 let alice = server
1888 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1889 .logged_in_with_oauth()
1890 .build()
1891 .await;
1892 alice
1893 .encryption()
1894 .bootstrap_cross_signing(None)
1895 .await
1896 .expect("Alice should be able to set up cross signing");
1897
1898 let oauth = alice.oauth();
1900 let grant = oauth
1901 .grant_login_with_qr_code()
1902 .device_creation_timeout(Duration::from_secs(2))
1903 .scan(&qr_code_data);
1904
1905 let mut updates = grant.subscribe_to_progress();
1907 let mut state = grant.state.get();
1908 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1909 let updates_task = spawn(async move {
1910 while let Some(update) = updates.next().await {
1911 match &update {
1912 GrantLoginProgress::Starting => {
1913 assert_matches!(state, GrantLoginProgress::Starting);
1914 }
1915 GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. }) => {
1916 assert_matches!(state, GrantLoginProgress::Starting);
1917 }
1918 _ => {
1919 panic!("Alice should abort the process");
1920 }
1921 }
1922 state = update;
1923 }
1924 });
1925
1926 assert_matches!(grant.await, Err(QRCodeGrantLoginError::NotFound));
1930 updates_task.await.expect("Alice should run through all progress states");
1931 }
1932}