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.to_base64().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.to_base64().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 use vodozemac::Curve25519PublicKey;
388
389 use super::*;
390 use crate::{
391 authentication::oauth::qrcode::{
392 LoginFailureReason, QrAuthMessage,
393 messages::{AuthorizationGrant, LoginProtocolType},
394 secure_channel::{EstablishedSecureChannel, test::MockedRendezvousServer},
395 },
396 http_client::HttpClient,
397 test_utils::mocks::MatrixMockServer,
398 };
399
400 enum BobBehaviour {
401 HappyPath,
402 UnexpectedMessageInsteadOfLoginProtocol,
403 DeviceAlreadyExists,
404 DeviceNotCreated,
405 }
406
407 #[allow(clippy::too_many_arguments)]
408 async fn request_login_with_scanned_qr_code(
409 behaviour: BobBehaviour,
410 qr_code_rx: oneshot::Receiver<QrCodeData>,
411 check_code_tx: oneshot::Sender<u8>,
412 server: MatrixMockServer,
413 _rendezvous_server: MockedRendezvousServer,
417 device_authorization_grant: Option<AuthorizationGrant>,
418 secrets_bundle: Option<SecretsBundle>,
419 ) {
420 let qr_code_data = qr_code_rx.await.expect("Bob should receive the QR code");
422
423 let mut bob = EstablishedSecureChannel::from_qr_code(
425 reqwest::Client::new(),
426 &qr_code_data,
427 QrCodeMode::Login,
428 )
429 .await
430 .expect("Bob should be able to connect the secure channel");
431
432 check_code_tx
434 .send(bob.check_code().to_digit())
435 .expect("Bob should be able to send the checkcode");
436
437 match behaviour {
438 BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol => {
439 let message = QrAuthMessage::LoginSuccess;
441 bob.send_json(message).await.unwrap();
442 return;
443 }
444 BobBehaviour::DeviceAlreadyExists => {
445 server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
448
449 let message = QrAuthMessage::LoginProtocol {
451 protocol: LoginProtocolType::DeviceAuthorizationGrant,
452 device_authorization_grant: device_authorization_grant
453 .expect("Bob needs the device authorization grant"),
454 device_id: Curve25519PublicKey::from_base64(
455 "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4",
456 )
457 .unwrap(),
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: Curve25519PublicKey::from_base64(
478 "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4",
479 )
480 .unwrap(),
481 };
482 bob.send_json(message).await.unwrap();
483 }
484 }
485
486 let message = bob
488 .receive_json()
489 .await
490 .expect("Bob should receive the LoginProtocolAccepted message from Alice");
491 assert_let!(QrAuthMessage::LoginProtocolAccepted = message);
492
493 match behaviour {
494 BobBehaviour::DeviceNotCreated => {
495 let message = QrAuthMessage::LoginSuccess;
500 bob.send_json(message).await.unwrap();
501
502 let message = bob
505 .receive_json()
506 .await
507 .expect("Bob should receive the LoginFailure message from Alice");
508 assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
509 assert_matches!(reason, LoginFailureReason::DeviceNotFound);
510
511 return; }
513 _ => {
514 server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
517
518 let message = QrAuthMessage::LoginSuccess;
520 bob.send_json(message).await.unwrap();
521 }
522 }
523
524 let message = bob
526 .receive_json()
527 .await
528 .expect("Bob should receive the LoginSecrets message from Alice");
529 assert_let!(QrAuthMessage::LoginSecrets(bundle) = message);
530
531 assert_eq!(
533 serde_json::to_value(&secrets_bundle).unwrap(),
534 serde_json::to_value(&bundle).unwrap()
535 );
536 }
537
538 #[allow(clippy::too_many_arguments)]
539 async fn request_login_with_generated_qr_code(
540 behaviour: BobBehaviour,
541 channel: SecureChannel,
542 check_code_rx: oneshot::Receiver<u8>,
543 server: MatrixMockServer,
544 _rendezvous_server: MockedRendezvousServer,
548 homeserver: Url,
549 device_authorization_grant: Option<AuthorizationGrant>,
550 secrets_bundle: Option<SecretsBundle>,
551 ) {
552 let channel =
554 channel.connect().await.expect("Bob should be able to connect the secure channel");
555
556 let check_code = check_code_rx.await.expect("Bob should receive the checkcode");
558 let mut bob = channel
559 .confirm(check_code)
560 .expect("Bob should be able to confirm the channel is secure");
561
562 let message = bob
564 .receive_json()
565 .await
566 .expect("Bob should receive the LoginProtocolAccepted message from Alice");
567 assert_let!(
568 QrAuthMessage::LoginProtocols { protocols, homeserver: alice_homeserver } = message
569 );
570 assert_eq!(protocols, vec![LoginProtocolType::DeviceAuthorizationGrant]);
571 assert_eq!(alice_homeserver, homeserver);
572
573 match behaviour {
574 BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol => {
575 let message = QrAuthMessage::LoginSuccess;
577 bob.send_json(message).await.unwrap();
578 return;
579 }
580 BobBehaviour::DeviceAlreadyExists => {
581 server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
584
585 let message = QrAuthMessage::LoginProtocol {
587 protocol: LoginProtocolType::DeviceAuthorizationGrant,
588 device_authorization_grant: device_authorization_grant
589 .expect("Bob needs the device authorization grant"),
590 device_id: Curve25519PublicKey::from_base64(
591 "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4",
592 )
593 .unwrap(),
594 };
595 bob.send_json(message).await.unwrap();
596
597 let message = bob
599 .receive_json()
600 .await
601 .expect("Bob should receive the LoginFailure message from Alice");
602 assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
603 assert_matches!(reason, LoginFailureReason::DeviceAlreadyExists);
604
605 return; }
607 _ => {
608 let message = QrAuthMessage::LoginProtocol {
610 protocol: LoginProtocolType::DeviceAuthorizationGrant,
611 device_authorization_grant: device_authorization_grant
612 .expect("Bob needs the device authorization grant"),
613 device_id: Curve25519PublicKey::from_base64(
614 "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4",
615 )
616 .unwrap(),
617 };
618 bob.send_json(message).await.unwrap();
619 }
620 }
621
622 let message = bob
624 .receive_json()
625 .await
626 .expect("Bob should receive the LoginProtocolAccepted message from Alice");
627 assert_let!(QrAuthMessage::LoginProtocolAccepted = message);
628
629 match behaviour {
630 BobBehaviour::DeviceNotCreated => {
631 let message = QrAuthMessage::LoginSuccess;
636 bob.send_json(message).await.unwrap();
637
638 let message = bob
641 .receive_json()
642 .await
643 .expect("Bob should receive the LoginFailure message from Alice");
644 assert_let!(QrAuthMessage::LoginFailure { reason, .. } = message);
645 assert_matches!(reason, LoginFailureReason::DeviceNotFound);
646
647 return; }
649 _ => {
650 server.mock_get_device().ok().expect(1..).named("get_device").mount().await;
653
654 let message = QrAuthMessage::LoginSuccess;
656 bob.send_json(message).await.unwrap();
657 }
658 }
659
660 let message = bob
662 .receive_json()
663 .await
664 .expect("Bob should receive the LoginSecrets message from Alice");
665 assert_let!(QrAuthMessage::LoginSecrets(bundle) = message);
666
667 assert_eq!(
669 serde_json::to_value(&secrets_bundle).unwrap(),
670 serde_json::to_value(&bundle).unwrap()
671 );
672 }
673
674 #[async_test]
675 async fn test_grant_login_with_generated_qr_code() {
676 let server = MatrixMockServer::new().await;
677 let rendezvous_server =
678 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
679 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
680
681 let device_authorization_grant = AuthorizationGrant {
682 verification_uri_complete: Some(VerificationUriComplete::new(
683 "https://id.matrix.org/device/abcde".to_owned(),
684 )),
685 verification_uri: EndUserVerificationUrl::new(
686 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
687 )
688 .unwrap(),
689 };
690
691 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
692 server
693 .mock_upload_cross_signing_keys()
694 .ok()
695 .expect(1)
696 .named("upload_xsigning_keys")
697 .mount()
698 .await;
699 server
700 .mock_upload_cross_signing_signatures()
701 .ok()
702 .expect(1)
703 .named("upload_xsigning_signatures")
704 .mount()
705 .await;
706
707 let user_id = owned_user_id!("@alice:example.org");
709 let device_id = owned_device_id!("ALICE_DEVICE");
710 let alice = server
711 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
712 .logged_in_with_oauth()
713 .build()
714 .await;
715 alice
716 .encryption()
717 .bootstrap_cross_signing(None)
718 .await
719 .expect("Alice should be able to set up cross signing");
720
721 let oauth = alice.oauth();
723 let grant = oauth
724 .grant_login_with_qr_code()
725 .device_creation_timeout(Duration::from_secs(2))
726 .generate();
727 let secrets_bundle = export_secrets_bundle(&alice)
728 .await
729 .expect("Alice should be able to export the secrets bundle");
730 let (qr_code_tx, qr_code_rx) = oneshot::channel();
731 let (checkcode_tx, checkcode_rx) = oneshot::channel();
732
733 let mut updates = grant.subscribe_to_progress();
735 let mut state = grant.state.get();
736 let verification_uri_complete =
737 device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
738 assert_matches!(state.clone(), GrantLoginProgress::Starting);
739 let updates_task = spawn(async move {
740 let mut qr_code_tx = Some(qr_code_tx);
741 let mut checkcode_rx = Some(checkcode_rx);
742
743 while let Some(update) = updates.next().await {
744 match &update {
745 GrantLoginProgress::Starting => {
746 assert_matches!(state, GrantLoginProgress::Starting);
747 }
748 GrantLoginProgress::EstablishingSecureChannel(
749 GeneratedQrProgress::QrReady(qr_code_data),
750 ) => {
751 assert_matches!(state, GrantLoginProgress::Starting);
752 qr_code_tx
753 .take()
754 .expect("The QR code should only be forwarded once")
755 .send(qr_code_data.clone())
756 .expect("Alice should be able to forward the QR code");
757 }
758 GrantLoginProgress::EstablishingSecureChannel(
759 GeneratedQrProgress::QrScanned(checkcode_sender),
760 ) => {
761 assert_matches!(
762 state,
763 GrantLoginProgress::EstablishingSecureChannel(
764 GeneratedQrProgress::QrReady(_)
765 )
766 );
767 let checkcode = checkcode_rx
768 .take()
769 .expect("The checkcode should only be forwarded once")
770 .await
771 .expect("Alice should receive the checkcode");
772 checkcode_sender
773 .send(checkcode)
774 .await
775 .expect("Alice should be able to forward the checkcode");
776 }
777 GrantLoginProgress::WaitingForAuth { verification_uri } => {
778 assert_matches!(
779 state,
780 GrantLoginProgress::EstablishingSecureChannel(
781 GeneratedQrProgress::QrScanned(_)
782 )
783 );
784 assert_eq!(verification_uri.as_str(), verification_uri_complete);
785 }
786 GrantLoginProgress::SyncingSecrets => {
787 assert_matches!(state, GrantLoginProgress::WaitingForAuth { .. });
788 }
789 GrantLoginProgress::Done => {
790 assert_matches!(state, GrantLoginProgress::SyncingSecrets);
791 break;
792 }
793 }
794 state = update;
795 }
796 });
797
798 let bob_task = spawn(async move {
800 request_login_with_scanned_qr_code(
801 BobBehaviour::HappyPath,
802 qr_code_rx,
803 checkcode_tx,
804 server,
805 rendezvous_server,
806 Some(device_authorization_grant),
807 Some(secrets_bundle),
808 )
809 .await;
810 });
811
812 grant.await.expect("Alice should be able to grant the login");
814 updates_task.await.expect("Alice should run through all progress states");
815 bob_task.await.expect("Bob's task should finish");
816 }
817
818 #[async_test]
819 async fn test_grant_login_with_scanned_qr_code() {
820 let server = MatrixMockServer::new().await;
821 let rendezvous_server =
822 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
823 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
824
825 let device_authorization_grant = AuthorizationGrant {
826 verification_uri_complete: Some(VerificationUriComplete::new(
827 "https://id.matrix.org/device/abcde".to_owned(),
828 )),
829 verification_uri: EndUserVerificationUrl::new(
830 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
831 )
832 .unwrap(),
833 };
834
835 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
836 server
837 .mock_upload_cross_signing_keys()
838 .ok()
839 .expect(1)
840 .named("upload_xsigning_keys")
841 .mount()
842 .await;
843 server
844 .mock_upload_cross_signing_signatures()
845 .ok()
846 .expect(1)
847 .named("upload_xsigning_signatures")
848 .mount()
849 .await;
850
851 let client = HttpClient::new(reqwest::Client::new(), Default::default());
853 let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
854 .await
855 .expect("Bob should be able to create a secure channel.");
856 let qr_code_data = channel.qr_code_data().clone();
857
858 let user_id = owned_user_id!("@alice:example.org");
860 let device_id = owned_device_id!("ALICE_DEVICE");
861 let alice = server
862 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
863 .logged_in_with_oauth()
864 .build()
865 .await;
866 alice
867 .encryption()
868 .bootstrap_cross_signing(None)
869 .await
870 .expect("Alice should be able to set up cross signing");
871
872 let oauth = alice.oauth();
874 let grant = oauth
875 .grant_login_with_qr_code()
876 .device_creation_timeout(Duration::from_secs(2))
877 .scan(&qr_code_data);
878 let secrets_bundle = export_secrets_bundle(&alice)
879 .await
880 .expect("Alice should be able to export the secrets bundle");
881 let (checkcode_tx, checkcode_rx) = oneshot::channel();
882
883 let mut updates = grant.subscribe_to_progress();
885 let mut state = grant.state.get();
886 let verification_uri_complete =
887 device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
888 assert_matches!(state.clone(), GrantLoginProgress::Starting);
889 let updates_task = spawn(async move {
890 let mut checkcode_tx = Some(checkcode_tx);
891
892 while let Some(update) = updates.next().await {
893 match &update {
894 GrantLoginProgress::Starting => {
895 assert_matches!(state, GrantLoginProgress::Starting);
896 }
897 GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
898 assert_matches!(state, GrantLoginProgress::Starting);
899 checkcode_tx
900 .take()
901 .expect("The checkcode should only be forwarded once")
902 .send(check_code.to_digit())
903 .expect("Alice should be able to forward the checkcode");
904 }
905 GrantLoginProgress::WaitingForAuth { verification_uri } => {
906 assert_matches!(
907 state,
908 GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
909 );
910 assert_eq!(verification_uri.as_str(), verification_uri_complete);
911 }
912 GrantLoginProgress::SyncingSecrets => {
913 assert_matches!(state, GrantLoginProgress::WaitingForAuth { .. });
914 }
915 GrantLoginProgress::Done => {
916 assert_matches!(state, GrantLoginProgress::SyncingSecrets);
917 break;
918 }
919 }
920 state = update;
921 }
922 });
923
924 let bob_task = spawn(async move {
926 request_login_with_generated_qr_code(
927 BobBehaviour::HappyPath,
928 channel,
929 checkcode_rx,
930 server,
931 rendezvous_server,
932 alice.homeserver(),
933 Some(device_authorization_grant),
934 Some(secrets_bundle),
935 )
936 .await;
937 });
938
939 grant.await.expect("Alice should be able to grant the login");
941 updates_task.await.expect("Alice should run through all progress states");
942 bob_task.await.expect("Bob's task should finish");
943 }
944
945 #[async_test]
946 async fn test_grant_login_with_scanned_qr_code_with_homeserver_swap() {
947 let server = MatrixMockServer::new().await;
948 let rendezvous_server =
949 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
950 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
951
952 let device_authorization_grant = AuthorizationGrant {
953 verification_uri_complete: Some(VerificationUriComplete::new(
954 "https://id.matrix.org/device/abcde".to_owned(),
955 )),
956 verification_uri: EndUserVerificationUrl::new(
957 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
958 )
959 .unwrap(),
960 };
961
962 let login_server = MatrixMockServer::new().await;
963
964 login_server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
965 login_server
966 .mock_upload_cross_signing_keys()
967 .ok()
968 .expect(1)
969 .named("upload_xsigning_keys")
970 .mount()
971 .await;
972 login_server
973 .mock_upload_cross_signing_signatures()
974 .ok()
975 .expect(1)
976 .named("upload_xsigning_signatures")
977 .mount()
978 .await;
979
980 let client = HttpClient::new(reqwest::Client::new(), Default::default());
982 let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
983 .await
984 .expect("Bob should be able to create a secure channel.");
985 let qr_code_data = channel.qr_code_data().clone();
986
987 let user_id = owned_user_id!("@alice:example.org");
989 let device_id = owned_device_id!("ALICE_DEVICE");
990 let alice = login_server
991 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
992 .logged_in_with_oauth()
993 .build()
994 .await;
995 alice
996 .encryption()
997 .bootstrap_cross_signing(None)
998 .await
999 .expect("Alice should be able to set up cross signing");
1000
1001 let oauth = alice.oauth();
1003 let grant = oauth
1004 .grant_login_with_qr_code()
1005 .device_creation_timeout(Duration::from_secs(2))
1006 .scan(&qr_code_data);
1007 let secrets_bundle = export_secrets_bundle(&alice)
1008 .await
1009 .expect("Alice should be able to export the secrets bundle");
1010 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1011
1012 let mut updates = grant.subscribe_to_progress();
1014 let mut state = grant.state.get();
1015 let verification_uri_complete =
1016 device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
1017 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1018 let updates_task = spawn(async move {
1019 let mut checkcode_tx = Some(checkcode_tx);
1020
1021 while let Some(update) = updates.next().await {
1022 match &update {
1023 GrantLoginProgress::Starting => {
1024 assert_matches!(state, GrantLoginProgress::Starting);
1025 }
1026 GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1027 assert_matches!(state, GrantLoginProgress::Starting);
1028 checkcode_tx
1029 .take()
1030 .expect("The checkcode should only be forwarded once")
1031 .send(check_code.to_digit())
1032 .expect("Alice should be able to forward the checkcode");
1033 }
1034 GrantLoginProgress::WaitingForAuth { verification_uri } => {
1035 assert_matches!(
1036 state,
1037 GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
1038 );
1039 assert_eq!(verification_uri.as_str(), verification_uri_complete);
1040 }
1041 GrantLoginProgress::SyncingSecrets => {
1042 assert_matches!(state, GrantLoginProgress::WaitingForAuth { .. });
1043 }
1044 GrantLoginProgress::Done => {
1045 assert_matches!(state, GrantLoginProgress::SyncingSecrets);
1046 break;
1047 }
1048 }
1049 state = update;
1050 }
1051 });
1052
1053 let bob_task = spawn(async move {
1055 request_login_with_generated_qr_code(
1056 BobBehaviour::HappyPath,
1057 channel,
1058 checkcode_rx,
1059 login_server,
1060 rendezvous_server,
1061 alice.homeserver(),
1062 Some(device_authorization_grant),
1063 Some(secrets_bundle),
1064 )
1065 .await;
1066 });
1067
1068 grant.await.expect("Alice should be able to grant the login");
1070 updates_task.await.expect("Alice should run through all progress states");
1071 bob_task.await.expect("Bob's task should finish");
1072 }
1073
1074 #[async_test]
1075 async fn test_grant_login_with_generated_qr_code_unexpected_message_instead_of_login_protocol()
1076 {
1077 let server = MatrixMockServer::new().await;
1078 let rendezvous_server =
1079 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1080 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1081
1082 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1083 server
1084 .mock_upload_cross_signing_keys()
1085 .ok()
1086 .expect(1)
1087 .named("upload_xsigning_keys")
1088 .mount()
1089 .await;
1090 server
1091 .mock_upload_cross_signing_signatures()
1092 .ok()
1093 .expect(1)
1094 .named("upload_xsigning_signatures")
1095 .mount()
1096 .await;
1097
1098 let user_id = owned_user_id!("@alice:example.org");
1100 let device_id = owned_device_id!("ALICE_DEVICE");
1101 let alice = server
1102 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1103 .logged_in_with_oauth()
1104 .build()
1105 .await;
1106 alice
1107 .encryption()
1108 .bootstrap_cross_signing(None)
1109 .await
1110 .expect("Alice should be able to set up cross signing");
1111
1112 let oauth = alice.oauth();
1114 let grant = oauth
1115 .grant_login_with_qr_code()
1116 .device_creation_timeout(Duration::from_secs(2))
1117 .generate();
1118 let (qr_code_tx, qr_code_rx) = oneshot::channel();
1119 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1120
1121 let mut updates = grant.subscribe_to_progress();
1123 let mut state = grant.state.get();
1124 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1125 let updates_task = spawn(async move {
1126 let mut qr_code_tx = Some(qr_code_tx);
1127 let mut checkcode_rx = Some(checkcode_rx);
1128
1129 while let Some(update) = updates.next().await {
1130 match &update {
1131 GrantLoginProgress::Starting => {
1132 assert_matches!(state, GrantLoginProgress::Starting);
1133 }
1134 GrantLoginProgress::EstablishingSecureChannel(
1135 GeneratedQrProgress::QrReady(qr_code_data),
1136 ) => {
1137 assert_matches!(state, GrantLoginProgress::Starting);
1138 qr_code_tx
1139 .take()
1140 .expect("The QR code should only be forwarded once")
1141 .send(qr_code_data.clone())
1142 .expect("Alice should be able to forward the QR code");
1143 }
1144 GrantLoginProgress::EstablishingSecureChannel(
1145 GeneratedQrProgress::QrScanned(checkcode_sender),
1146 ) => {
1147 assert_matches!(
1148 state,
1149 GrantLoginProgress::EstablishingSecureChannel(
1150 GeneratedQrProgress::QrReady(_)
1151 )
1152 );
1153 let checkcode = checkcode_rx
1154 .take()
1155 .expect("The checkcode should only be forwarded once")
1156 .await
1157 .expect("Alice should receive the checkcode");
1158 checkcode_sender
1159 .send(checkcode)
1160 .await
1161 .expect("Alice should be able to forward the checkcode");
1162 break;
1163 }
1164 _ => {
1165 panic!("Alice should abort the process");
1166 }
1167 }
1168 state = update;
1169 }
1170 });
1171
1172 let bob_task = spawn(async move {
1174 request_login_with_scanned_qr_code(
1175 BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol,
1176 qr_code_rx,
1177 checkcode_tx,
1178 server,
1179 rendezvous_server,
1180 None,
1181 None,
1182 )
1183 .await;
1184 });
1185
1186 grant.await.expect_err("Alice should abort the login");
1188 updates_task.await.expect("Alice should run through all progress states");
1189 bob_task.await.expect("Bob's task should finish");
1190 }
1191
1192 #[async_test]
1193 async fn test_grant_login_with_scanned_qr_code_unexpected_message_instead_of_login_protocol() {
1194 let server = MatrixMockServer::new().await;
1195 let rendezvous_server =
1196 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1197 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1198
1199 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1200 server
1201 .mock_upload_cross_signing_keys()
1202 .ok()
1203 .expect(1)
1204 .named("upload_xsigning_keys")
1205 .mount()
1206 .await;
1207 server
1208 .mock_upload_cross_signing_signatures()
1209 .ok()
1210 .expect(1)
1211 .named("upload_xsigning_signatures")
1212 .mount()
1213 .await;
1214
1215 let client = HttpClient::new(reqwest::Client::new(), Default::default());
1217 let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1218 .await
1219 .expect("Bob should be able to create a secure channel.");
1220 let qr_code_data = channel.qr_code_data().clone();
1221
1222 let user_id = owned_user_id!("@alice:example.org");
1224 let device_id = owned_device_id!("ALICE_DEVICE");
1225 let alice = server
1226 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1227 .logged_in_with_oauth()
1228 .build()
1229 .await;
1230 alice
1231 .encryption()
1232 .bootstrap_cross_signing(None)
1233 .await
1234 .expect("Alice should be able to set up cross signing");
1235
1236 let oauth = alice.oauth();
1238 let grant = oauth
1239 .grant_login_with_qr_code()
1240 .device_creation_timeout(Duration::from_secs(2))
1241 .scan(&qr_code_data);
1242 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1243
1244 let mut updates = grant.subscribe_to_progress();
1246 let mut state = grant.state.get();
1247 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1248 let updates_task = spawn(async move {
1249 let mut checkcode_tx = Some(checkcode_tx);
1250
1251 while let Some(update) = updates.next().await {
1252 match &update {
1253 GrantLoginProgress::Starting => {
1254 assert_matches!(state, GrantLoginProgress::Starting);
1255 }
1256 GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1257 assert_matches!(state, GrantLoginProgress::Starting);
1258 checkcode_tx
1259 .take()
1260 .expect("The checkcode should only be forwarded once")
1261 .send(check_code.to_digit())
1262 .expect("Alice should be able to forward the checkcode");
1263 break;
1264 }
1265 _ => {
1266 panic!("Alice should abort the process");
1267 }
1268 }
1269 state = update;
1270 }
1271 });
1272
1273 let bob_task = spawn(async move {
1275 request_login_with_generated_qr_code(
1276 BobBehaviour::UnexpectedMessageInsteadOfLoginProtocol,
1277 channel,
1278 checkcode_rx,
1279 server,
1280 rendezvous_server,
1281 alice.homeserver(),
1282 None,
1283 None,
1284 )
1285 .await;
1286 });
1287
1288 grant.await.expect_err("Alice should abort the login");
1289
1290 updates_task.await.expect("Alice should run through all progress states");
1292 bob_task.await.expect("Bob's task should finish");
1293 }
1294
1295 #[async_test]
1296 async fn test_grant_login_with_generated_qr_code_device_already_exists() {
1297 let server = MatrixMockServer::new().await;
1298 let rendezvous_server =
1299 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1300 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1301
1302 let device_authorization_grant = AuthorizationGrant {
1303 verification_uri_complete: Some(VerificationUriComplete::new(
1304 "https://id.matrix.org/device/abcde".to_owned(),
1305 )),
1306 verification_uri: EndUserVerificationUrl::new(
1307 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1308 )
1309 .unwrap(),
1310 };
1311
1312 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1313 server
1314 .mock_upload_cross_signing_keys()
1315 .ok()
1316 .expect(1)
1317 .named("upload_xsigning_keys")
1318 .mount()
1319 .await;
1320 server
1321 .mock_upload_cross_signing_signatures()
1322 .ok()
1323 .expect(1)
1324 .named("upload_xsigning_signatures")
1325 .mount()
1326 .await;
1327
1328 let user_id = owned_user_id!("@alice:example.org");
1330 let device_id = owned_device_id!("ALICE_DEVICE");
1331 let alice = server
1332 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1333 .logged_in_with_oauth()
1334 .build()
1335 .await;
1336 alice
1337 .encryption()
1338 .bootstrap_cross_signing(None)
1339 .await
1340 .expect("Alice should be able to set up cross signing");
1341
1342 let oauth = alice.oauth();
1344 let grant = oauth
1345 .grant_login_with_qr_code()
1346 .device_creation_timeout(Duration::from_secs(2))
1347 .generate();
1348 let (qr_code_tx, qr_code_rx) = oneshot::channel();
1349 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1350
1351 let mut updates = grant.subscribe_to_progress();
1353 let mut state = grant.state.get();
1354 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1355 let updates_task = spawn(async move {
1356 let mut qr_code_tx = Some(qr_code_tx);
1357 let mut checkcode_rx = Some(checkcode_rx);
1358
1359 while let Some(update) = updates.next().await {
1360 match &update {
1361 GrantLoginProgress::Starting => {
1362 assert_matches!(state, GrantLoginProgress::Starting);
1363 }
1364 GrantLoginProgress::EstablishingSecureChannel(
1365 GeneratedQrProgress::QrReady(qr_code_data),
1366 ) => {
1367 assert_matches!(state, GrantLoginProgress::Starting);
1368 qr_code_tx
1369 .take()
1370 .expect("The QR code should only be forwarded once")
1371 .send(qr_code_data.clone())
1372 .expect("Alice should be able to forward the QR code");
1373 }
1374 GrantLoginProgress::EstablishingSecureChannel(
1375 GeneratedQrProgress::QrScanned(checkcode_sender),
1376 ) => {
1377 assert_matches!(
1378 state,
1379 GrantLoginProgress::EstablishingSecureChannel(
1380 GeneratedQrProgress::QrReady(_)
1381 )
1382 );
1383 let checkcode = checkcode_rx
1384 .take()
1385 .expect("The checkcode should only be forwarded once")
1386 .await
1387 .expect("Alice should receive the checkcode");
1388 checkcode_sender
1389 .send(checkcode)
1390 .await
1391 .expect("Alice should be able to forward the checkcode");
1392 }
1393 _ => {
1394 panic!("Alice should abort the process");
1395 }
1396 }
1397 state = update;
1398 }
1399 });
1400
1401 let bob_task = spawn(async move {
1403 request_login_with_scanned_qr_code(
1404 BobBehaviour::DeviceAlreadyExists,
1405 qr_code_rx,
1406 checkcode_tx,
1407 server,
1408 rendezvous_server,
1409 Some(device_authorization_grant),
1410 None,
1411 )
1412 .await;
1413 });
1414
1415 grant.await.expect_err("Alice should abort the login");
1417 updates_task.await.expect("Alice should run through all progress states");
1418 bob_task.await.expect("Bob's task should finish");
1419 }
1420
1421 #[async_test]
1422 async fn test_grant_login_with_scanned_qr_code_device_already_exists() {
1423 let server = MatrixMockServer::new().await;
1424 let rendezvous_server =
1425 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1426 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1427
1428 let device_authorization_grant = AuthorizationGrant {
1429 verification_uri_complete: Some(VerificationUriComplete::new(
1430 "https://id.matrix.org/device/abcde".to_owned(),
1431 )),
1432 verification_uri: EndUserVerificationUrl::new(
1433 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1434 )
1435 .unwrap(),
1436 };
1437
1438 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1439 server
1440 .mock_upload_cross_signing_keys()
1441 .ok()
1442 .expect(1)
1443 .named("upload_xsigning_keys")
1444 .mount()
1445 .await;
1446 server
1447 .mock_upload_cross_signing_signatures()
1448 .ok()
1449 .expect(1)
1450 .named("upload_xsigning_signatures")
1451 .mount()
1452 .await;
1453
1454 let client = HttpClient::new(reqwest::Client::new(), Default::default());
1456 let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1457 .await
1458 .expect("Bob should be able to create a secure channel.");
1459 let qr_code_data = channel.qr_code_data().clone();
1460
1461 let user_id = owned_user_id!("@alice:example.org");
1463 let device_id = owned_device_id!("ALICE_DEVICE");
1464 let alice = server
1465 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1466 .logged_in_with_oauth()
1467 .build()
1468 .await;
1469 alice
1470 .encryption()
1471 .bootstrap_cross_signing(None)
1472 .await
1473 .expect("Alice should be able to set up cross signing");
1474
1475 let oauth = alice.oauth();
1477 let grant = oauth
1478 .grant_login_with_qr_code()
1479 .device_creation_timeout(Duration::from_secs(2))
1480 .scan(&qr_code_data);
1481 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1482
1483 let mut updates = grant.subscribe_to_progress();
1485 let mut state = grant.state.get();
1486 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1487 let updates_task = spawn(async move {
1488 let mut checkcode_tx = Some(checkcode_tx);
1489
1490 while let Some(update) = updates.next().await {
1491 match &update {
1492 GrantLoginProgress::Starting => {
1493 assert_matches!(state, GrantLoginProgress::Starting);
1494 }
1495 GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1496 assert_matches!(state, GrantLoginProgress::Starting);
1497 checkcode_tx
1498 .take()
1499 .expect("The checkcode should only be forwarded once")
1500 .send(check_code.to_digit())
1501 .expect("Alice should be able to forward the checkcode");
1502 }
1503 _ => {
1504 panic!("Alice should abort the process");
1505 }
1506 }
1507 state = update;
1508 }
1509 });
1510
1511 let bob_task = spawn(async move {
1513 request_login_with_generated_qr_code(
1514 BobBehaviour::DeviceAlreadyExists,
1515 channel,
1516 checkcode_rx,
1517 server,
1518 rendezvous_server,
1519 alice.homeserver(),
1520 Some(device_authorization_grant),
1521 None,
1522 )
1523 .await;
1524 });
1525
1526 grant.await.expect_err("Alice should abort the login");
1528 updates_task.await.expect("Alice should run through all progress states");
1529 bob_task.await.expect("Bob's task should finish");
1530 }
1531
1532 #[async_test]
1533 async fn test_grant_login_with_generated_qr_code_device_not_created() {
1534 let server = MatrixMockServer::new().await;
1535 let rendezvous_server =
1536 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1537 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1538
1539 let device_authorization_grant = AuthorizationGrant {
1540 verification_uri_complete: Some(VerificationUriComplete::new(
1541 "https://id.matrix.org/device/abcde".to_owned(),
1542 )),
1543 verification_uri: EndUserVerificationUrl::new(
1544 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1545 )
1546 .unwrap(),
1547 };
1548
1549 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1550 server
1551 .mock_upload_cross_signing_keys()
1552 .ok()
1553 .expect(1)
1554 .named("upload_xsigning_keys")
1555 .mount()
1556 .await;
1557 server
1558 .mock_upload_cross_signing_signatures()
1559 .ok()
1560 .expect(1)
1561 .named("upload_xsigning_signatures")
1562 .mount()
1563 .await;
1564
1565 let user_id = owned_user_id!("@alice:example.org");
1567 let device_id = owned_device_id!("ALICE_DEVICE");
1568 let alice = server
1569 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1570 .logged_in_with_oauth()
1571 .build()
1572 .await;
1573 alice
1574 .encryption()
1575 .bootstrap_cross_signing(None)
1576 .await
1577 .expect("Alice should be able to set up cross signing");
1578
1579 let oauth = alice.oauth();
1581 let grant = oauth
1582 .grant_login_with_qr_code()
1583 .device_creation_timeout(Duration::from_secs(2))
1584 .generate();
1585 let (qr_code_tx, qr_code_rx) = oneshot::channel();
1586 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1587
1588 let mut updates = grant.subscribe_to_progress();
1590 let mut state = grant.state.get();
1591 let verification_uri_complete =
1592 device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
1593 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1594 let updates_task = spawn(async move {
1595 let mut qr_code_tx = Some(qr_code_tx);
1596 let mut checkcode_rx = Some(checkcode_rx);
1597
1598 while let Some(update) = updates.next().await {
1599 match &update {
1600 GrantLoginProgress::Starting => {
1601 assert_matches!(state, GrantLoginProgress::Starting);
1602 }
1603 GrantLoginProgress::EstablishingSecureChannel(
1604 GeneratedQrProgress::QrReady(qr_code_data),
1605 ) => {
1606 assert_matches!(state, GrantLoginProgress::Starting);
1607 qr_code_tx
1608 .take()
1609 .expect("The QR code should only be forwarded once")
1610 .send(qr_code_data.clone())
1611 .expect("Alice should be able to forward the QR code");
1612 }
1613 GrantLoginProgress::EstablishingSecureChannel(
1614 GeneratedQrProgress::QrScanned(checkcode_sender),
1615 ) => {
1616 assert_matches!(
1617 state,
1618 GrantLoginProgress::EstablishingSecureChannel(
1619 GeneratedQrProgress::QrReady(_)
1620 )
1621 );
1622 let checkcode = checkcode_rx
1623 .take()
1624 .expect("The checkcode should only be forwarded once")
1625 .await
1626 .expect("Alice should receive the checkcode");
1627 checkcode_sender
1628 .send(checkcode)
1629 .await
1630 .expect("Alice should be able to forward the checkcode");
1631 }
1632 GrantLoginProgress::WaitingForAuth { verification_uri } => {
1633 assert_matches!(
1634 state,
1635 GrantLoginProgress::EstablishingSecureChannel(
1636 GeneratedQrProgress::QrScanned(_)
1637 )
1638 );
1639 assert_eq!(verification_uri.as_str(), verification_uri_complete);
1640 }
1641 _ => {
1642 panic!("Alice should abort the process");
1643 }
1644 }
1645 state = update;
1646 }
1647 });
1648
1649 let bob_task = spawn(async move {
1651 request_login_with_scanned_qr_code(
1652 BobBehaviour::DeviceNotCreated,
1653 qr_code_rx,
1654 checkcode_tx,
1655 server,
1656 rendezvous_server,
1657 Some(device_authorization_grant),
1658 None,
1659 )
1660 .await;
1661 });
1662
1663 grant.await.expect_err("Alice should abort the login");
1664 updates_task.await.expect("Alice should run through all progress states");
1665 bob_task.await.expect("Bob's task should finish");
1666 }
1667
1668 #[async_test]
1669 async fn test_grant_login_with_scanned_qr_code_device_not_created() {
1670 let server = MatrixMockServer::new().await;
1671 let rendezvous_server =
1672 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1673 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1674
1675 let device_authorization_grant = AuthorizationGrant {
1676 verification_uri_complete: Some(VerificationUriComplete::new(
1677 "https://id.matrix.org/device/abcde".to_owned(),
1678 )),
1679 verification_uri: EndUserVerificationUrl::new(
1680 "https://id.matrix.org/device/abcde?code=ABCDE".to_owned(),
1681 )
1682 .unwrap(),
1683 };
1684
1685 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1686 server
1687 .mock_upload_cross_signing_keys()
1688 .ok()
1689 .expect(1)
1690 .named("upload_xsigning_keys")
1691 .mount()
1692 .await;
1693 server
1694 .mock_upload_cross_signing_signatures()
1695 .ok()
1696 .expect(1)
1697 .named("upload_xsigning_signatures")
1698 .mount()
1699 .await;
1700
1701 let client = HttpClient::new(reqwest::Client::new(), Default::default());
1703 let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1704 .await
1705 .expect("Bob should be able to create a secure channel.");
1706 let qr_code_data = channel.qr_code_data().clone();
1707
1708 let user_id = owned_user_id!("@alice:example.org");
1710 let device_id = owned_device_id!("ALICE_DEVICE");
1711 let alice = server
1712 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1713 .logged_in_with_oauth()
1714 .build()
1715 .await;
1716 alice
1717 .encryption()
1718 .bootstrap_cross_signing(None)
1719 .await
1720 .expect("Alice should be able to set up cross signing");
1721
1722 let oauth = alice.oauth();
1724 let grant = oauth
1725 .grant_login_with_qr_code()
1726 .device_creation_timeout(Duration::from_secs(2))
1727 .scan(&qr_code_data);
1728 let (checkcode_tx, checkcode_rx) = oneshot::channel();
1729
1730 let mut updates = grant.subscribe_to_progress();
1732 let mut state = grant.state.get();
1733 let verification_uri_complete =
1734 device_authorization_grant.clone().verification_uri_complete.unwrap().into_secret();
1735 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1736 let updates_task = spawn(async move {
1737 let mut checkcode_tx = Some(checkcode_tx);
1738
1739 while let Some(update) = updates.next().await {
1740 match &update {
1741 GrantLoginProgress::Starting => {
1742 assert_matches!(state, GrantLoginProgress::Starting);
1743 }
1744 GrantLoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1745 assert_matches!(state, GrantLoginProgress::Starting);
1746 checkcode_tx
1747 .take()
1748 .expect("The checkcode should only be forwarded once")
1749 .send(check_code.to_digit())
1750 .expect("Alice should be able to forward the checkcode");
1751 }
1752 GrantLoginProgress::WaitingForAuth { verification_uri } => {
1753 assert_matches!(
1754 state,
1755 GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. })
1756 );
1757 assert_eq!(verification_uri.as_str(), verification_uri_complete);
1758 }
1759 _ => {
1760 panic!("Alice should abort the process");
1761 }
1762 }
1763 state = update;
1764 }
1765 });
1766
1767 let bob_task = spawn(async move {
1769 request_login_with_generated_qr_code(
1770 BobBehaviour::DeviceNotCreated,
1771 channel,
1772 checkcode_rx,
1773 server,
1774 rendezvous_server,
1775 alice.homeserver(),
1776 Some(device_authorization_grant),
1777 None,
1778 )
1779 .await;
1780 });
1781
1782 grant.await.expect_err("Alice should abort the login");
1783 updates_task.await.expect("Alice should run through all progress states");
1784 bob_task.await.expect("Bob's task should finish");
1785 }
1786
1787 #[async_test]
1788 async fn test_grant_login_with_generated_qr_code_session_expired() {
1789 let server = MatrixMockServer::new().await;
1790 let rendezvous_server =
1791 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::from_secs(2))
1792 .await;
1793 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1794
1795 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1796 server
1797 .mock_upload_cross_signing_keys()
1798 .ok()
1799 .expect(1)
1800 .named("upload_xsigning_keys")
1801 .mount()
1802 .await;
1803 server
1804 .mock_upload_cross_signing_signatures()
1805 .ok()
1806 .expect(1)
1807 .named("upload_xsigning_signatures")
1808 .mount()
1809 .await;
1810
1811 let user_id = owned_user_id!("@alice:example.org");
1813 let device_id = owned_device_id!("ALICE_DEVICE");
1814 let alice = server
1815 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1816 .logged_in_with_oauth()
1817 .build()
1818 .await;
1819 alice
1820 .encryption()
1821 .bootstrap_cross_signing(None)
1822 .await
1823 .expect("Alice should be able to set up cross signing");
1824
1825 let oauth = alice.oauth();
1827 let grant = oauth
1828 .grant_login_with_qr_code()
1829 .device_creation_timeout(Duration::from_secs(2))
1830 .generate();
1831
1832 let mut updates = grant.subscribe_to_progress();
1834 let mut state = grant.state.get();
1835 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1836 let updates_task = spawn(async move {
1837 while let Some(update) = updates.next().await {
1838 match &update {
1839 GrantLoginProgress::Starting => {
1840 assert_matches!(state, GrantLoginProgress::Starting);
1841 }
1842 GrantLoginProgress::EstablishingSecureChannel(
1843 GeneratedQrProgress::QrReady(_),
1844 ) => {
1845 assert_matches!(state, GrantLoginProgress::Starting);
1846 }
1847 _ => {
1848 panic!("Alice should abort the process");
1849 }
1850 }
1851 state = update;
1852 }
1853 });
1854
1855 assert_matches!(grant.await, Err(QRCodeGrantLoginError::NotFound));
1859 updates_task.await.expect("Alice should run through all progress states");
1860 }
1861
1862 #[async_test]
1863 async fn test_grant_login_with_scanned_qr_code_session_expired() {
1864 let server = MatrixMockServer::new().await;
1865 let rendezvous_server =
1866 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::from_secs(2))
1867 .await;
1868 debug!("Set up rendezvous server mock at {}", rendezvous_server.rendezvous_url);
1869
1870 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
1871 server
1872 .mock_upload_cross_signing_keys()
1873 .ok()
1874 .expect(1)
1875 .named("upload_xsigning_keys")
1876 .mount()
1877 .await;
1878 server
1879 .mock_upload_cross_signing_signatures()
1880 .ok()
1881 .expect(1)
1882 .named("upload_xsigning_signatures")
1883 .mount()
1884 .await;
1885
1886 let client = HttpClient::new(reqwest::Client::new(), Default::default());
1888 let channel = SecureChannel::login(client, &rendezvous_server.homeserver_url)
1889 .await
1890 .expect("Bob should be able to create a secure channel.");
1891 let qr_code_data = channel.qr_code_data().clone();
1892
1893 let user_id = owned_user_id!("@alice:example.org");
1895 let device_id = owned_device_id!("ALICE_DEVICE");
1896 let alice = server
1897 .client_builder_for_crypto_end_to_end(&user_id, &device_id)
1898 .logged_in_with_oauth()
1899 .build()
1900 .await;
1901 alice
1902 .encryption()
1903 .bootstrap_cross_signing(None)
1904 .await
1905 .expect("Alice should be able to set up cross signing");
1906
1907 let oauth = alice.oauth();
1909 let grant = oauth
1910 .grant_login_with_qr_code()
1911 .device_creation_timeout(Duration::from_secs(2))
1912 .scan(&qr_code_data);
1913
1914 let mut updates = grant.subscribe_to_progress();
1916 let mut state = grant.state.get();
1917 assert_matches!(state.clone(), GrantLoginProgress::Starting);
1918 let updates_task = spawn(async move {
1919 while let Some(update) = updates.next().await {
1920 match &update {
1921 GrantLoginProgress::Starting => {
1922 assert_matches!(state, GrantLoginProgress::Starting);
1923 }
1924 GrantLoginProgress::EstablishingSecureChannel(QrProgress { .. }) => {
1925 assert_matches!(state, GrantLoginProgress::Starting);
1926 }
1927 _ => {
1928 panic!("Alice should abort the process");
1929 }
1930 }
1931 state = update;
1932 }
1933 });
1934
1935 assert_matches!(grant.await, Err(QRCodeGrantLoginError::NotFound));
1939 updates_task.await.expect("Alice should run through all progress states");
1940 }
1941}