1use std::future::IntoFuture;
16
17use eyeball::SharedObservable;
18use futures_core::Stream;
19use matrix_sdk_base::{
20 SessionMeta, boxed_into_future,
21 crypto::types::qr_login::{QrCodeData, QrCodeIntent},
22 store::RoomLoadSettings,
23};
24use oauth2::{DeviceCodeErrorResponseType, StandardDeviceAuthorizationResponse};
25use ruma::{
26 OwnedDeviceId,
27 api::client::discovery::get_authorization_server_metadata::v1::AuthorizationServerMetadata,
28};
29use tracing::trace;
30use vodozemac::Curve25519PublicKey;
31#[cfg(doc)]
32use vodozemac::ecies::CheckCode;
33
34use super::{
35 DeviceAuthorizationOAuthError, QRCodeLoginError, SecureChannelError,
36 messages::{LoginFailureReason, QrAuthMessage},
37 secure_channel::{EstablishedSecureChannel, SecureChannel},
38};
39use crate::{
40 Client,
41 authentication::oauth::{
42 ClientRegistrationData, OAuth, OAuthError,
43 qrcode::{CheckCodeSender, GeneratedQrProgress, LoginProtocolType, QrProgress},
44 },
45};
46
47async fn send_unexpected_message_error(
48 channel: &mut EstablishedSecureChannel,
49) -> Result<(), SecureChannelError> {
50 channel
51 .send_json(QrAuthMessage::LoginFailure {
52 reason: LoginFailureReason::UnexpectedMessageReceived,
53 homeserver: None,
54 })
55 .await
56}
57
58async fn finish_login<Q>(
59 client: &Client,
60 mut channel: EstablishedSecureChannel,
61 registration_data: Option<&ClientRegistrationData>,
62 state: SharedObservable<LoginProgress<Q>>,
63) -> Result<(), QRCodeLoginError> {
64 let oauth = client.oauth();
65
66 trace!("Registering the client with the OAuth 2.0 authorization server.");
68 let server_metadata = register_client(&oauth, registration_data).await?;
69
70 let account = vodozemac::olm::Account::new();
73 let public_key = account.identity_keys().curve25519;
74 let device_id = public_key;
75
76 trace!("Requesting device authorization.");
79 let auth_grant_response =
80 request_device_authorization(&oauth, &server_metadata, device_id).await?;
81
82 trace!("Letting the existing device know about the device authorization grant.");
85 let message =
86 QrAuthMessage::authorization_grant_login_protocol((&auth_grant_response).into(), device_id);
87 channel.send_json(&message).await?;
88
89 match channel.receive_json().await? {
91 QrAuthMessage::LoginProtocolAccepted => (),
92 QrAuthMessage::LoginFailure { reason, homeserver } => {
93 return Err(QRCodeLoginError::LoginFailure { reason, homeserver });
94 }
95 message => {
96 send_unexpected_message_error(&mut channel).await?;
97
98 return Err(QRCodeLoginError::UnexpectedMessage {
99 expected: "m.login.protocol_accepted",
100 received: message,
101 });
102 }
103 }
104
105 let user_code = auth_grant_response.user_code();
109 state.set(LoginProgress::WaitingForToken { user_code: user_code.secret().to_owned() });
110
111 trace!("Waiting for the OAuth 2.0 authorization server to give us the access token.");
114 if let Err(e) = wait_for_tokens(&oauth, &server_metadata, &auth_grant_response).await {
115 if let Some(e) = e.as_request_token_error() {
118 match e {
119 DeviceCodeErrorResponseType::AccessDenied => {
120 channel.send_json(QrAuthMessage::LoginDeclined).await?;
121 }
122 DeviceCodeErrorResponseType::ExpiredToken => {
123 channel
124 .send_json(QrAuthMessage::LoginFailure {
125 reason: LoginFailureReason::AuthorizationExpired,
126 homeserver: None,
127 })
128 .await?;
129 }
130 _ => (),
131 }
132 }
133
134 return Err(e.into());
135 }
136
137 trace!("Discovering our own user id.");
143 let whoami_response = client.whoami().await.map_err(QRCodeLoginError::UserIdDiscovery)?;
144 client
145 .base_client()
146 .activate(
147 SessionMeta {
148 user_id: whoami_response.user_id,
149 device_id: OwnedDeviceId::from(device_id.to_base64()),
150 },
151 RoomLoadSettings::default(),
152 Some(account),
153 )
154 .await
155 .map_err(|error| QRCodeLoginError::SessionTokens(error.into()))?;
156
157 client.oauth().enable_cross_process_lock().await?;
158
159 state.set(LoginProgress::SyncingSecrets);
160
161 trace!("Telling the existing device that we successfully logged in.");
163 let message = QrAuthMessage::LoginSuccess;
164 channel.send_json(&message).await?;
165
166 trace!("Waiting for the secrets bundle.");
169 let bundle = match channel.receive_json().await? {
170 QrAuthMessage::LoginSecrets(bundle) => bundle,
171 QrAuthMessage::LoginFailure { reason, homeserver } => {
172 return Err(QRCodeLoginError::LoginFailure { reason, homeserver });
173 }
174 message => {
175 send_unexpected_message_error(&mut channel).await?;
176
177 return Err(QRCodeLoginError::UnexpectedMessage {
178 expected: "m.login.secrets",
179 received: message,
180 });
181 }
182 };
183
184 client.encryption().import_secrets_bundle(&bundle).await?;
187
188 client
191 .encryption()
192 .ensure_device_keys_upload()
193 .await
194 .map_err(QRCodeLoginError::DeviceKeyUpload)?;
195
196 client.encryption().spawn_initialization_task(None).await;
201 client.encryption().wait_for_e2ee_initialization_tasks().await;
202
203 trace!("successfully logged in and enabled E2EE.");
204
205 state.set(LoginProgress::Done);
207
208 Ok(())
210}
211
212async fn register_client(
216 oauth: &OAuth,
217 registration_data: Option<&ClientRegistrationData>,
218) -> Result<AuthorizationServerMetadata, DeviceAuthorizationOAuthError> {
219 let server_metadata = oauth.server_metadata().await.map_err(OAuthError::from)?;
220 oauth.use_registration_data(&server_metadata, registration_data).await?;
221
222 Ok(server_metadata)
223}
224
225async fn request_device_authorization(
226 oauth: &OAuth,
227 server_metadata: &AuthorizationServerMetadata,
228 device_id: Curve25519PublicKey,
229) -> Result<StandardDeviceAuthorizationResponse, DeviceAuthorizationOAuthError> {
230 let response = oauth
231 .request_device_authorization(server_metadata, Some(device_id.to_base64().into()))
232 .await?;
233 Ok(response)
234}
235
236async fn wait_for_tokens(
237 oauth: &OAuth,
238 server_metadata: &AuthorizationServerMetadata,
239 auth_response: &StandardDeviceAuthorizationResponse,
240) -> Result<(), DeviceAuthorizationOAuthError> {
241 oauth.exchange_device_code(server_metadata, auth_response).await?;
242 Ok(())
243}
244
245#[derive(Clone, Debug, Default)]
247pub enum LoginProgress<Q> {
248 #[default]
250 Starting,
251 EstablishingSecureChannel(Q),
254 WaitingForToken {
258 user_code: String,
262 },
263 SyncingSecrets,
265 Done,
267}
268
269#[derive(Debug)]
272pub struct LoginWithQrCode<'a> {
273 client: &'a Client,
274 registration_data: Option<&'a ClientRegistrationData>,
275 qr_code_data: &'a QrCodeData,
276 state: SharedObservable<LoginProgress<QrProgress>>,
277}
278
279impl LoginWithQrCode<'_> {
280 pub fn subscribe_to_progress(&self) -> impl Stream<Item = LoginProgress<QrProgress>> + use<> {
286 self.state.subscribe()
287 }
288}
289
290impl<'a> IntoFuture for LoginWithQrCode<'a> {
291 type Output = Result<(), QRCodeLoginError>;
292 boxed_into_future!(extra_bounds: 'a);
293
294 fn into_future(self) -> Self::IntoFuture {
295 Box::pin(async move {
296 let channel = self.establish_secure_channel().await?;
305
306 trace!("Established the secure channel.");
307
308 let check_code = channel.check_code().to_owned();
312 self.state.set(LoginProgress::EstablishingSecureChannel(QrProgress { check_code }));
313
314 finish_login(self.client, channel, self.registration_data, self.state).await
321 })
322 }
323}
324
325impl<'a> LoginWithQrCode<'a> {
326 pub(crate) fn new(
327 client: &'a Client,
328 qr_code_data: &'a QrCodeData,
329 registration_data: Option<&'a ClientRegistrationData>,
330 ) -> LoginWithQrCode<'a> {
331 LoginWithQrCode { client, registration_data, qr_code_data, state: Default::default() }
332 }
333
334 async fn establish_secure_channel(
335 &self,
336 ) -> Result<EstablishedSecureChannel, SecureChannelError> {
337 let http_client = self.client.inner.http_client.inner.clone();
338
339 let channel = EstablishedSecureChannel::from_qr_code(
340 http_client,
341 self.qr_code_data,
342 QrCodeIntent::Login,
343 )
344 .await?;
345
346 Ok(channel)
347 }
348}
349
350#[derive(Debug)]
353pub struct LoginWithGeneratedQrCode<'a> {
354 client: &'a Client,
355 registration_data: Option<&'a ClientRegistrationData>,
356 state: SharedObservable<LoginProgress<GeneratedQrProgress>>,
357}
358
359impl LoginWithGeneratedQrCode<'_> {
360 pub fn subscribe_to_progress(
365 &self,
366 ) -> impl Stream<Item = LoginProgress<GeneratedQrProgress>> + use<> {
367 self.state.subscribe()
368 }
369}
370
371impl<'a> IntoFuture for LoginWithGeneratedQrCode<'a> {
372 type Output = Result<(), QRCodeLoginError>;
373 boxed_into_future!(extra_bounds: 'a);
374
375 fn into_future(self) -> Self::IntoFuture {
376 Box::pin(async move {
377 let mut channel = self.establish_secure_channel().await?;
380
381 trace!("Established the secure channel.");
382
383 let message = channel.receive_json().await?;
387
388 let homeserver = match message {
391 QrAuthMessage::LoginProtocols { protocols, homeserver } => {
392 if !protocols.contains(&LoginProtocolType::DeviceAuthorizationGrant) {
393 channel
394 .send_json(QrAuthMessage::LoginFailure {
395 reason: LoginFailureReason::UnsupportedProtocol,
396 homeserver: None,
397 })
398 .await?;
399
400 return Err(QRCodeLoginError::LoginFailure {
401 reason: LoginFailureReason::UnsupportedProtocol,
402 homeserver: None,
403 });
404 }
405
406 homeserver
407 }
408 _ => {
409 send_unexpected_message_error(&mut channel).await?;
410
411 return Err(QRCodeLoginError::UnexpectedMessage {
412 expected: "m.login.protocols",
413 received: message,
414 });
415 }
416 };
417
418 if self.client.homeserver() != homeserver {
421 self.client
422 .switch_homeserver_and_re_resolve_well_known(homeserver)
423 .await
424 .map_err(QRCodeLoginError::ServerReset)?;
425 }
426
427 finish_login(self.client, channel, self.registration_data, self.state).await
430 })
431 }
432}
433
434impl<'a> LoginWithGeneratedQrCode<'a> {
435 pub(crate) fn new(
436 client: &'a Client,
437 registration_data: Option<&'a ClientRegistrationData>,
438 ) -> Self {
439 Self { client, registration_data, state: Default::default() }
440 }
441
442 async fn establish_secure_channel(
443 &self,
444 ) -> Result<EstablishedSecureChannel, SecureChannelError> {
445 let http_client = self.client.inner.http_client.clone();
446
447 let secure_channel = SecureChannel::login(http_client, &self.client.homeserver()).await?;
451
452 let qr_code_data = secure_channel.qr_code_data().clone();
456 trace!("Generated QR code.");
457 self.state.set(LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrReady(
458 qr_code_data,
459 )));
460
461 let channel = secure_channel.connect().await?;
465
466 trace!("Waiting for checkcode.");
471 let (tx, rx) = tokio::sync::oneshot::channel();
472 self.state.set(LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrScanned(
473 CheckCodeSender::new(tx),
474 )));
475
476 let check_code = rx.await.map_err(|_| SecureChannelError::CannotReceiveCheckCode)?;
480 trace!("Received check code.");
481 channel.confirm(check_code)
482 }
483}
484
485#[cfg(all(test, not(target_family = "wasm")))]
486mod test {
487 use std::time::Duration;
488
489 use assert_matches2::{assert_let, assert_matches};
490 use futures_util::StreamExt;
491 use matrix_sdk_base::crypto::types::{
492 SecretsBundle,
493 qr_login::{Msc4108IntentData, QrCodeIntentData},
494 };
495 use matrix_sdk_common::executor::spawn;
496 use matrix_sdk_test::async_test;
497 use serde_json::json;
498 use vodozemac::ecies::CheckCode;
499
500 use super::*;
501 use crate::{
502 authentication::oauth::qrcode::{
503 messages::LoginProtocolType,
504 secure_channel::{SecureChannel, test::MockedRendezvousServer},
505 },
506 config::RequestConfig,
507 http_client::HttpClient,
508 test_utils::{client::oauth::mock_client_metadata, mocks::MatrixMockServer},
509 };
510
511 enum AliceBehaviour {
512 HappyPath,
513 DeclinedProtocol,
514 UnexpectedMessage,
515 UnexpectedMessageInsteadOfSecrets,
516 RefuseSecrets,
517 LetSessionExpire,
518 }
519
520 enum TokenResponse {
522 Ok,
523 AccessDenied,
524 ExpiredToken,
525 }
526
527 fn secrets_bundle() -> SecretsBundle {
528 let json = json!({
529 "cross_signing": {
530 "master_key": "rTtSv67XGS6k/rg6/yTG/m573cyFTPFRqluFhQY+hSw",
531 "self_signing_key": "4jbPt7jh5D2iyM4U+3IDa+WthgJB87IQN1ATdkau+xk",
532 "user_signing_key": "YkFKtkjcsTxF6UAzIIG/l6Nog/G2RigCRfWj3cjNWeM",
533 },
534 });
535
536 serde_json::from_value(json).expect("We should be able to deserialize a secrets bundle")
537 }
538
539 async fn grant_login(
542 alice: SecureChannel,
543 check_code_receiver: tokio::sync::oneshot::Receiver<CheckCode>,
544 behavior: AliceBehaviour,
545 ) {
546 let alice = alice.connect().await.expect("Alice should be able to connect the channel");
547
548 let check_code =
549 check_code_receiver.await.expect("We should receive the check code from bob");
550
551 let mut alice = alice
552 .confirm(check_code.to_digit())
553 .expect("Alice should be able to confirm the secure channel");
554
555 let message = alice
556 .receive_json()
557 .await
558 .expect("Alice should be able to receive the initial message from Bob");
559
560 assert_let!(QrAuthMessage::LoginProtocol { protocol, .. } = message);
561 assert_eq!(protocol, LoginProtocolType::DeviceAuthorizationGrant);
562
563 let message = match behavior {
564 AliceBehaviour::DeclinedProtocol => QrAuthMessage::LoginFailure {
565 reason: LoginFailureReason::UnsupportedProtocol,
566 homeserver: None,
567 },
568 AliceBehaviour::UnexpectedMessage => QrAuthMessage::LoginDeclined,
569 _ => QrAuthMessage::LoginProtocolAccepted,
570 };
571
572 alice.send_json(message).await.unwrap();
573
574 let message: QrAuthMessage = alice.receive_json().await.unwrap();
575 assert_let!(QrAuthMessage::LoginSuccess = message);
576
577 let message = match behavior {
578 AliceBehaviour::UnexpectedMessageInsteadOfSecrets => QrAuthMessage::LoginDeclined,
579 AliceBehaviour::RefuseSecrets => QrAuthMessage::LoginFailure {
580 reason: LoginFailureReason::DeviceNotFound,
581 homeserver: None,
582 },
583 _ => QrAuthMessage::LoginSecrets(secrets_bundle()),
584 };
585
586 alice.send_json(message).await.unwrap();
587 }
588
589 #[async_test]
590 async fn test_qr_login() {
591 let server = MatrixMockServer::new().await;
592 let rendezvous_server =
593 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
594 let (sender, receiver) = tokio::sync::oneshot::channel();
595
596 let oauth_server = server.oauth();
597 oauth_server.mock_server_metadata().ok().expect(1).named("server_metadata").mount().await;
598 oauth_server.mock_registration().ok().expect(1).named("registration").mount().await;
599 oauth_server
600 .mock_device_authorization()
601 .ok()
602 .expect(1)
603 .named("device_authorization")
604 .mount()
605 .await;
606 oauth_server.mock_token().ok().expect(1).named("token").mount().await;
607
608 server.mock_versions().ok().expect(1..).named("versions").mount().await;
609 server.mock_who_am_i().ok().expect(1).named("whoami").mount().await;
610 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
611 server.mock_query_keys().ok().expect(1).named("query_keys").mount().await;
612
613 let client = HttpClient::new(reqwest::Client::new(), Default::default());
614 let alice = SecureChannel::reciprocate(client, &rendezvous_server.homeserver_url)
615 .await
616 .expect("Alice should be able to create a secure channel.");
617
618 assert_let!(
619 QrCodeIntentData::Msc4108 {
620 data: Msc4108IntentData::Reciprocate { server_name },
621 ..
622 } = &alice.qr_code_data().intent_data()
623 );
624
625 let bob = Client::builder()
626 .server_name_or_homeserver_url(server_name)
627 .request_config(RequestConfig::new().disable_retry())
628 .build()
629 .await
630 .expect("We should be able to build the Client object from the URL in the QR code");
631
632 let qr_code = alice.qr_code_data().clone();
633
634 let oauth = bob.oauth();
635 let registration_data = mock_client_metadata().into();
636 let login_bob = oauth.login_with_qr_code(Some(®istration_data)).scan(&qr_code);
637 let mut updates = login_bob.subscribe_to_progress();
638
639 let updates_task = spawn(async move {
640 let mut sender = Some(sender);
641
642 while let Some(update) = updates.next().await {
643 match update {
644 LoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
645 sender
646 .take()
647 .expect("The establishing secure channel update should be received only once")
648 .send(check_code)
649 .expect("Bob should be able to send the check code to Alice");
650 }
651 LoginProgress::Done => break,
652 _ => (),
653 }
654 }
655 });
656 let alice_task =
657 spawn(async { grant_login(alice, receiver, AliceBehaviour::HappyPath).await });
658
659 login_bob.await.expect("Bob should be able to login");
661 alice_task.await.expect("Alice should have completed it's task successfully");
662 updates_task.await.unwrap();
663
664 assert!(bob.encryption().cross_signing_status().await.unwrap().is_complete());
665 let own_identity =
666 bob.encryption().get_user_identity(bob.user_id().unwrap()).await.unwrap().unwrap();
667
668 assert!(own_identity.is_verified());
669 }
670
671 async fn grant_login_with_generated_qr(
672 alice: &Client,
673 qr_receiver: tokio::sync::oneshot::Receiver<QrCodeData>,
674 cctx_receiver: tokio::sync::oneshot::Receiver<CheckCodeSender>,
675 behavior: AliceBehaviour,
676 ) {
677 let qr_code_data = qr_receiver.await.expect("Alice should receive the QR code");
678
679 let mut channel = EstablishedSecureChannel::from_qr_code(
680 alice.inner.http_client.inner.clone(),
681 &qr_code_data,
682 QrCodeIntent::Reciprocate,
683 )
684 .await
685 .expect("Alice should be able to establish the secure channel");
686
687 trace!("Established the secure channel.");
688
689 let check_code = channel.check_code().to_digit();
692
693 let check_code_sender =
694 cctx_receiver.await.expect("Alice should receive the CheckCodeSender");
695
696 check_code_sender
697 .send(check_code)
698 .await
699 .expect("Alice should be able to send the check code to Bob");
700
701 let message = QrAuthMessage::LoginProtocols {
703 protocols: vec![LoginProtocolType::DeviceAuthorizationGrant],
704 homeserver: alice.homeserver(),
705 };
706 channel
707 .send_json(message)
708 .await
709 .expect("Alice should be able to send the `m.login.protocols` message to Bob");
710
711 let message: QrAuthMessage = channel
713 .receive_json()
714 .await
715 .expect("Alice should be able to receive the `m.login.protocol` message from Bob");
716 assert_let!(QrAuthMessage::LoginProtocol { protocol, .. } = message);
717 assert_eq!(protocol, LoginProtocolType::DeviceAuthorizationGrant);
718
719 let message = match behavior {
721 AliceBehaviour::DeclinedProtocol => QrAuthMessage::LoginFailure {
722 reason: LoginFailureReason::UnsupportedProtocol,
723 homeserver: None,
724 },
725 AliceBehaviour::UnexpectedMessage => QrAuthMessage::LoginDeclined,
726 _ => QrAuthMessage::LoginProtocolAccepted,
727 };
728 channel
729 .send_json(message)
730 .await
731 .expect("Alice should be able to send the `m.login.protocol_accepted` message to Bob");
732
733 let message: QrAuthMessage = channel
734 .receive_json()
735 .await
736 .expect("Alice should be able to receive the `m.login.success` message from Bob");
737 assert_let!(QrAuthMessage::LoginSuccess = message);
738
739 let message = match behavior {
741 AliceBehaviour::UnexpectedMessageInsteadOfSecrets => QrAuthMessage::LoginDeclined,
742 AliceBehaviour::RefuseSecrets => QrAuthMessage::LoginFailure {
743 reason: LoginFailureReason::DeviceNotFound,
744 homeserver: None,
745 },
746 _ => QrAuthMessage::LoginSecrets(secrets_bundle()),
747 };
748 channel
749 .send_json(message)
750 .await
751 .expect("Alice should be able to send the `m.login.secrets` message to Bob");
752 }
753
754 #[async_test]
755 async fn test_generated_qr_login() {
756 let server = MatrixMockServer::new().await;
757 let rendezvous_server =
758 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
759 let (qr_sender, qr_receiver) = tokio::sync::oneshot::channel();
760 let (cctx_sender, cctx_receiver) = tokio::sync::oneshot::channel();
761
762 let oauth_server = server.oauth();
763 oauth_server.mock_server_metadata().ok().expect(1).named("server_metadata").mount().await;
764 oauth_server.mock_registration().ok().expect(1).named("registration").mount().await;
765 oauth_server
766 .mock_device_authorization()
767 .ok()
768 .expect(1)
769 .named("device_authorization")
770 .mount()
771 .await;
772 oauth_server.mock_token().ok().expect(1).named("token").mount().await;
773
774 server.mock_versions().ok().expect(1..).named("versions").mount().await;
775 server.mock_who_am_i().ok().expect(1).named("whoami").mount().await;
776 server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
777 server.mock_query_keys().ok().expect(1).named("query_keys").mount().await;
778
779 let homeserver_url = rendezvous_server.homeserver_url.clone();
780
781 let alice = server.client_builder().logged_in_with_oauth().build().await;
784 assert!(alice.session_meta().is_some(), "Alice should be logged in");
785
786 let bob = Client::builder()
788 .server_name_or_homeserver_url(&homeserver_url)
789 .request_config(RequestConfig::new().disable_retry())
790 .build()
791 .await
792 .expect("Should be able to create a client for Bob");
793
794 let secure_channel = SecureChannel::login(bob.inner.http_client.clone(), &homeserver_url)
795 .await
796 .expect("Bob should be able to create a secure channel");
797
798 assert_matches!(
799 secure_channel.qr_code_data().intent_data(),
800 QrCodeIntentData::Msc4108 { data: Msc4108IntentData::Login, .. }
801 );
802
803 let registration_data = mock_client_metadata().into();
804 let bob_oauth = bob.oauth();
805 let bob_login = bob_oauth.login_with_qr_code(Some(®istration_data)).generate();
806 let mut bob_updates = bob_login.subscribe_to_progress();
807
808 let updates_task = spawn(async move {
809 let mut qr_sender = Some(qr_sender);
810 let mut cctx_sender = Some(cctx_sender);
811
812 while let Some(update) = bob_updates.next().await {
813 match update {
814 LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrReady(qr)) => {
815 qr_sender
816 .take()
817 .expect("The establishing secure channel update with a qr code should be received only once")
818 .send(qr)
819 .expect("Bob should be able to send the qr code code to Alice");
820 }
821 LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrScanned(
822 cctx,
823 )) => {
824 cctx_sender
825 .take()
826 .expect("The establishing secure channel update with a CheckCodeSender should be received only once")
827 .send(cctx)
828 .expect("Bob should be able to send the qr code code to Alice");
829 }
830 LoginProgress::Done => break,
831 _ => (),
832 }
833 }
834 });
835
836 let alice_task = spawn(async move {
837 grant_login_with_generated_qr(
838 &alice,
839 qr_receiver,
840 cctx_receiver,
841 AliceBehaviour::HappyPath,
842 )
843 .await
844 });
845
846 bob_login.await.expect("Bob should be able to login");
848 alice_task.await.expect("Alice should have completed it's task successfully");
849 updates_task.await.unwrap();
850
851 assert!(bob.encryption().cross_signing_status().await.unwrap().is_complete());
852 let own_identity =
853 bob.encryption().get_user_identity(bob.user_id().unwrap()).await.unwrap().unwrap();
854
855 assert!(own_identity.is_verified());
856 }
857
858 #[async_test]
859 async fn test_generated_qr_login_with_homeserver_swap() {
860 let server = MatrixMockServer::new().await;
861 let rendezvous_server =
862 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
863 let (qr_sender, qr_receiver) = tokio::sync::oneshot::channel();
864 let (cctx_sender, cctx_receiver) = tokio::sync::oneshot::channel();
865
866 let login_server = MatrixMockServer::new().await;
867 let oauth_server = login_server.oauth();
868 oauth_server.mock_server_metadata().ok().expect(1).named("server_metadata").mount().await;
869 oauth_server.mock_registration().ok().expect(1).named("registration").mount().await;
870 oauth_server
871 .mock_device_authorization()
872 .ok()
873 .expect(1)
874 .named("device_authorization")
875 .mount()
876 .await;
877 oauth_server.mock_token().ok().expect(1).named("token").mount().await;
878
879 server.mock_versions().ok().expect(1..).named("versions").mount().await;
880
881 login_server.mock_well_known().ok().expect(1).named("well_known").mount().await;
882 login_server.mock_versions().ok().expect(1..).named("versions").mount().await;
883 login_server.mock_who_am_i().ok().expect(1).named("whoami").mount().await;
884 login_server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
885 login_server.mock_query_keys().ok().expect(1).named("query_keys").mount().await;
886
887 let homeserver_url = rendezvous_server.homeserver_url.clone();
888
889 let alice = login_server.client_builder().logged_in_with_oauth().build().await;
892 assert!(alice.session_meta().is_some(), "Alice should be logged in");
893
894 let bob = Client::builder()
896 .server_name_or_homeserver_url(&homeserver_url)
897 .request_config(RequestConfig::new().disable_retry())
898 .build()
899 .await
900 .expect("Should be able to create a client for Bob");
901
902 let secure_channel = SecureChannel::login(bob.inner.http_client.clone(), &homeserver_url)
903 .await
904 .expect("Bob should be able to create a secure channel");
905
906 assert_matches!(
907 secure_channel.qr_code_data().intent_data(),
908 QrCodeIntentData::Msc4108 { data: Msc4108IntentData::Login, .. }
909 );
910
911 let registration_data = mock_client_metadata().into();
912 let bob_oauth = bob.oauth();
913 let bob_login = bob_oauth.login_with_qr_code(Some(®istration_data)).generate();
914 let mut bob_updates = bob_login.subscribe_to_progress();
915
916 let updates_task = spawn(async move {
917 let mut qr_sender = Some(qr_sender);
918 let mut cctx_sender = Some(cctx_sender);
919
920 while let Some(update) = bob_updates.next().await {
921 match update {
922 LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrReady(qr)) => {
923 qr_sender
924 .take()
925 .expect("The establishing secure channel update with a qr code should be received only once")
926 .send(qr)
927 .expect("Bob should be able to send the qr code code to Alice");
928 }
929 LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrScanned(
930 cctx,
931 )) => {
932 cctx_sender
933 .take()
934 .expect("The establishing secure channel update with a CheckCodeSender should be received only once")
935 .send(cctx)
936 .expect("Bob should be able to send the qr code code to Alice");
937 }
938 LoginProgress::Done => break,
939 _ => (),
940 }
941 }
942 });
943
944 let alice_task = spawn(async move {
945 grant_login_with_generated_qr(
946 &alice,
947 qr_receiver,
948 cctx_receiver,
949 AliceBehaviour::HappyPath,
950 )
951 .await
952 });
953
954 bob_login.await.expect("Bob should be able to login");
956 alice_task.await.expect("Alice should have completed it's task successfully");
957 updates_task.await.unwrap();
958
959 assert!(bob.encryption().cross_signing_status().await.unwrap().is_complete());
960 let own_identity =
961 bob.encryption().get_user_identity(bob.user_id().unwrap()).await.unwrap().unwrap();
962
963 assert!(own_identity.is_verified());
964 }
965
966 async fn test_failure(
967 token_response: TokenResponse,
968 alice_behavior: AliceBehaviour,
969 ) -> Result<(), QRCodeLoginError> {
970 let server = MatrixMockServer::new().await;
971 let expiration = match alice_behavior {
972 AliceBehaviour::LetSessionExpire => Duration::from_secs(2),
973 _ => Duration::MAX,
974 };
975 let rendezvous_server =
976 MockedRendezvousServer::new(server.server(), "abcdEFG12345", expiration).await;
977 let (sender, receiver) = tokio::sync::oneshot::channel();
978
979 let oauth_server = server.oauth();
980 let expected_calls = match alice_behavior {
981 AliceBehaviour::LetSessionExpire => 0,
982 _ => 1,
983 };
984 oauth_server
985 .mock_server_metadata()
986 .ok()
987 .expect(expected_calls)
988 .named("server_metadata")
989 .mount()
990 .await;
991 oauth_server
992 .mock_registration()
993 .ok()
994 .expect(expected_calls)
995 .named("registration")
996 .mount()
997 .await;
998 oauth_server
999 .mock_device_authorization()
1000 .ok()
1001 .expect(expected_calls)
1002 .named("device_authorization")
1003 .mount()
1004 .await;
1005
1006 let token_mock = oauth_server.mock_token();
1007 let token_mock = match token_response {
1008 TokenResponse::Ok => token_mock.ok(),
1009 TokenResponse::AccessDenied => token_mock.access_denied(),
1010 TokenResponse::ExpiredToken => token_mock.expired_token(),
1011 };
1012 token_mock.named("token").mount().await;
1013
1014 server.mock_versions().ok().named("versions").mount().await;
1015 server.mock_who_am_i().ok().named("whoami").mount().await;
1016
1017 let client = HttpClient::new(reqwest::Client::new(), Default::default());
1018 let alice = SecureChannel::reciprocate(client, &rendezvous_server.homeserver_url)
1019 .await
1020 .expect("Alice should be able to create a secure channel.");
1021
1022 assert_let!(
1023 QrCodeIntentData::Msc4108 {
1024 data: Msc4108IntentData::Reciprocate { server_name },
1025 ..
1026 } = &alice.qr_code_data().intent_data()
1027 );
1028
1029 let bob = Client::builder()
1030 .server_name_or_homeserver_url(server_name)
1031 .request_config(RequestConfig::new().disable_retry())
1032 .build()
1033 .await
1034 .expect("We should be able to build the Client object from the URL in the QR code");
1035
1036 let qr_code = alice.qr_code_data().clone();
1037
1038 let oauth = bob.oauth();
1039 let registration_data = mock_client_metadata().into();
1040 let login_bob = oauth.login_with_qr_code(Some(®istration_data)).scan(&qr_code);
1041 let mut updates = login_bob.subscribe_to_progress();
1042
1043 let _updates_task = spawn(async move {
1044 let mut sender = Some(sender);
1045
1046 while let Some(update) = updates.next().await {
1047 match update {
1048 LoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1049 sender
1050 .take()
1051 .expect("The establishing secure channel update should be received only once")
1052 .send(check_code)
1053 .expect("Bob should be able to send the check code to Alice");
1054 }
1055 LoginProgress::Done => break,
1056 _ => (),
1057 }
1058 }
1059 });
1060
1061 if !matches!(alice_behavior, AliceBehaviour::LetSessionExpire) {
1062 let _alice_task =
1063 spawn(async move { grant_login(alice, receiver, alice_behavior).await });
1064 }
1065
1066 login_bob.await
1067 }
1068
1069 async fn test_generated_failure(
1070 token_response: TokenResponse,
1071 alice_behavior: AliceBehaviour,
1072 ) -> Result<(), QRCodeLoginError> {
1073 let server = MatrixMockServer::new().await;
1074 let expiration = match alice_behavior {
1075 AliceBehaviour::LetSessionExpire => Duration::from_secs(2),
1076 _ => Duration::MAX,
1077 };
1078 let rendezvous_server =
1079 MockedRendezvousServer::new(server.server(), "abcdEFG12345", expiration).await;
1080
1081 let (qr_sender, qr_receiver) = tokio::sync::oneshot::channel();
1082 let (cctx_sender, cctx_receiver) = tokio::sync::oneshot::channel();
1083
1084 let oauth_server = server.oauth();
1085 let expected_calls = match alice_behavior {
1086 AliceBehaviour::LetSessionExpire => 0,
1087 _ => 1,
1088 };
1089 oauth_server
1090 .mock_server_metadata()
1091 .ok()
1092 .expect(expected_calls)
1093 .named("server_metadata")
1094 .mount()
1095 .await;
1096 oauth_server
1097 .mock_registration()
1098 .ok()
1099 .expect(expected_calls)
1100 .named("registration")
1101 .mount()
1102 .await;
1103 oauth_server
1104 .mock_device_authorization()
1105 .ok()
1106 .expect(expected_calls)
1107 .named("device_authorization")
1108 .mount()
1109 .await;
1110
1111 let token_mock = oauth_server.mock_token();
1112 let token_mock = match token_response {
1113 TokenResponse::Ok => token_mock.ok(),
1114 TokenResponse::AccessDenied => token_mock.access_denied(),
1115 TokenResponse::ExpiredToken => token_mock.expired_token(),
1116 };
1117 token_mock.named("token").mount().await;
1118
1119 server.mock_versions().ok().named("versions").mount().await;
1120 server.mock_who_am_i().ok().named("whoami").mount().await;
1121
1122 let homeserver_url = rendezvous_server.homeserver_url.clone();
1123
1124 let alice = server.client_builder().logged_in_with_oauth().build().await;
1127 assert!(alice.session_meta().is_some(), "Alice should be logged in");
1128
1129 let bob = Client::builder()
1131 .server_name_or_homeserver_url(&homeserver_url)
1132 .request_config(RequestConfig::new().disable_retry())
1133 .build()
1134 .await
1135 .expect("Should be able to create a client for Bob");
1136
1137 let secure_channel = SecureChannel::login(bob.inner.http_client.clone(), &homeserver_url)
1138 .await
1139 .expect("Bob should be able to create a secure channel");
1140
1141 assert_matches!(
1142 secure_channel.qr_code_data().intent_data(),
1143 QrCodeIntentData::Msc4108 { data: Msc4108IntentData::Login, .. }
1144 );
1145
1146 let registration_data = mock_client_metadata().into();
1147 let bob_oauth = bob.oauth();
1148 let bob_login = bob_oauth.login_with_qr_code(Some(®istration_data)).generate();
1149 let mut bob_updates = bob_login.subscribe_to_progress();
1150
1151 let _updates_task = spawn(async move {
1152 let mut qr_sender = Some(qr_sender);
1153 let mut cctx_sender = Some(cctx_sender);
1154
1155 while let Some(update) = bob_updates.next().await {
1156 match update {
1157 LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrReady(qr)) => {
1158 qr_sender
1159 .take()
1160 .expect("The establishing secure channel update with a qr code should be received only once")
1161 .send(qr)
1162 .expect("Bob should be able to send the qr code code to Alice");
1163 }
1164 LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrScanned(
1165 cctx,
1166 )) => {
1167 cctx_sender
1168 .take()
1169 .expect("The establishing secure channel update with a CheckCodeSender should be received only once")
1170 .send(cctx)
1171 .expect("Bob should be able to send the qr code code to Alice");
1172 }
1173 LoginProgress::Done => break,
1174 _ => (),
1175 }
1176 }
1177 });
1178
1179 if !matches!(alice_behavior, AliceBehaviour::LetSessionExpire) {
1180 let _alice_task = spawn(async move {
1181 grant_login_with_generated_qr(&alice, qr_receiver, cctx_receiver, alice_behavior)
1182 .await
1183 });
1184 }
1185
1186 bob_login.await
1187 }
1188
1189 #[async_test]
1190 async fn test_qr_login_refused_access_token() {
1191 let result = test_failure(TokenResponse::AccessDenied, AliceBehaviour::HappyPath).await;
1192
1193 assert_let!(Err(QRCodeLoginError::OAuth(e)) = result);
1194 assert_eq!(
1195 e.as_request_token_error(),
1196 Some(&DeviceCodeErrorResponseType::AccessDenied),
1197 "The server should have told us that access has been denied."
1198 );
1199 }
1200
1201 #[async_test]
1202 async fn test_generated_qr_login_refused_access_token() {
1203 let result =
1204 test_generated_failure(TokenResponse::AccessDenied, AliceBehaviour::HappyPath).await;
1205
1206 assert_let!(Err(QRCodeLoginError::OAuth(e)) = result);
1207 assert_eq!(
1208 e.as_request_token_error(),
1209 Some(&DeviceCodeErrorResponseType::AccessDenied),
1210 "The server should have told us that access has been denied."
1211 );
1212 }
1213
1214 #[async_test]
1215 async fn test_qr_login_expired_token() {
1216 let result = test_failure(TokenResponse::ExpiredToken, AliceBehaviour::HappyPath).await;
1217
1218 assert_let!(Err(QRCodeLoginError::OAuth(e)) = result);
1219 assert_eq!(
1220 e.as_request_token_error(),
1221 Some(&DeviceCodeErrorResponseType::ExpiredToken),
1222 "The server should have told us that access has been denied."
1223 );
1224 }
1225
1226 #[async_test]
1227 async fn test_generated_qr_login_expired_token() {
1228 let result =
1229 test_generated_failure(TokenResponse::ExpiredToken, AliceBehaviour::HappyPath).await;
1230
1231 assert_let!(Err(QRCodeLoginError::OAuth(e)) = result);
1232 assert_eq!(
1233 e.as_request_token_error(),
1234 Some(&DeviceCodeErrorResponseType::ExpiredToken),
1235 "The server should have told us that access has been denied."
1236 );
1237 }
1238
1239 #[async_test]
1240 async fn test_qr_login_declined_protocol() {
1241 let result = test_failure(TokenResponse::Ok, AliceBehaviour::DeclinedProtocol).await;
1242
1243 assert_let!(Err(QRCodeLoginError::LoginFailure { reason, .. }) = result);
1244 assert_eq!(
1245 reason,
1246 LoginFailureReason::UnsupportedProtocol,
1247 "Alice should have told us that the protocol is unsupported."
1248 );
1249 }
1250
1251 #[async_test]
1252 async fn test_generated_qr_login_declined_protocol() {
1253 let result =
1254 test_generated_failure(TokenResponse::Ok, AliceBehaviour::DeclinedProtocol).await;
1255
1256 assert_let!(Err(QRCodeLoginError::LoginFailure { reason, .. }) = result);
1257 assert_eq!(
1258 reason,
1259 LoginFailureReason::UnsupportedProtocol,
1260 "Alice should have told us that the protocol is unsupported."
1261 );
1262 }
1263
1264 #[async_test]
1265 async fn test_qr_login_unexpected_message() {
1266 let result = test_failure(TokenResponse::Ok, AliceBehaviour::UnexpectedMessage).await;
1267
1268 assert_let!(Err(QRCodeLoginError::UnexpectedMessage { expected, .. }) = result);
1269 assert_eq!(expected, "m.login.protocol_accepted");
1270 }
1271
1272 #[async_test]
1273 async fn test_generated_qr_login_unexpected_message() {
1274 let result =
1275 test_generated_failure(TokenResponse::Ok, AliceBehaviour::UnexpectedMessage).await;
1276
1277 assert_let!(Err(QRCodeLoginError::UnexpectedMessage { expected, .. }) = result);
1278 assert_eq!(expected, "m.login.protocol_accepted");
1279 }
1280
1281 #[async_test]
1282 async fn test_qr_login_unexpected_message_instead_of_secrets() {
1283 let result =
1284 test_failure(TokenResponse::Ok, AliceBehaviour::UnexpectedMessageInsteadOfSecrets)
1285 .await;
1286
1287 assert_let!(Err(QRCodeLoginError::UnexpectedMessage { expected, .. }) = result);
1288 assert_eq!(expected, "m.login.secrets");
1289 }
1290
1291 #[async_test]
1292 async fn test_generated_qr_login_unexpected_message_instead_of_secrets() {
1293 let result = test_generated_failure(
1294 TokenResponse::Ok,
1295 AliceBehaviour::UnexpectedMessageInsteadOfSecrets,
1296 )
1297 .await;
1298
1299 assert_let!(Err(QRCodeLoginError::UnexpectedMessage { expected, .. }) = result);
1300 assert_eq!(expected, "m.login.secrets");
1301 }
1302
1303 #[async_test]
1304 async fn test_qr_login_refuse_secrets() {
1305 let result = test_failure(TokenResponse::Ok, AliceBehaviour::RefuseSecrets).await;
1306
1307 assert_let!(Err(QRCodeLoginError::LoginFailure { reason, .. }) = result);
1308 assert_eq!(reason, LoginFailureReason::DeviceNotFound);
1309 }
1310
1311 #[async_test]
1312 async fn test_generated_qr_login_refuse_secrets() {
1313 let result = test_generated_failure(TokenResponse::Ok, AliceBehaviour::RefuseSecrets).await;
1314
1315 assert_let!(Err(QRCodeLoginError::LoginFailure { reason, .. }) = result);
1316 assert_eq!(reason, LoginFailureReason::DeviceNotFound);
1317 }
1318
1319 #[async_test]
1320 async fn test_qr_login_session_expired() {
1321 let result = test_failure(TokenResponse::Ok, AliceBehaviour::LetSessionExpire).await;
1322
1323 assert_matches!(result, Err(QRCodeLoginError::NotFound));
1324 }
1325
1326 #[async_test]
1327 async fn test_generated_qr_login_session_expired() {
1328 let result =
1329 test_generated_failure(TokenResponse::Ok, AliceBehaviour::LetSessionExpire).await;
1330
1331 assert_matches!(result, Err(QRCodeLoginError::NotFound));
1332 }
1333
1334 #[async_test]
1335 async fn test_device_authorization_endpoint_missing() {
1336 let server = MatrixMockServer::new().await;
1337 let rendezvous_server =
1338 MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1339 let (sender, receiver) = tokio::sync::oneshot::channel();
1340
1341 let oauth_server = server.oauth();
1342 oauth_server
1343 .mock_server_metadata()
1344 .ok_without_device_authorization()
1345 .expect(1)
1346 .named("server_metadata")
1347 .mount()
1348 .await;
1349 oauth_server.mock_registration().ok().expect(1).named("registration").mount().await;
1350
1351 server.mock_versions().ok().named("versions").mount().await;
1352 server.mock_who_am_i().ok().named("whoami").mount().await;
1353
1354 let client = HttpClient::new(reqwest::Client::new(), Default::default());
1355 let alice = SecureChannel::reciprocate(client, &rendezvous_server.homeserver_url)
1356 .await
1357 .expect("Alice should be able to create a secure channel.");
1358
1359 assert_let!(
1360 QrCodeIntentData::Msc4108 {
1361 data: Msc4108IntentData::Reciprocate { server_name },
1362 ..
1363 } = &alice.qr_code_data().intent_data()
1364 );
1365
1366 let bob = Client::builder()
1367 .server_name_or_homeserver_url(server_name)
1368 .request_config(RequestConfig::new().disable_retry())
1369 .build()
1370 .await
1371 .expect("We should be able to build the Client object from the URL in the QR code");
1372
1373 let qr_code = alice.qr_code_data().clone();
1374
1375 let oauth = bob.oauth();
1376 let registration_data = mock_client_metadata().into();
1377 let login_bob = oauth.login_with_qr_code(Some(®istration_data)).scan(&qr_code);
1378 let mut updates = login_bob.subscribe_to_progress();
1379
1380 let _updates_task = spawn(async move {
1381 let mut sender = Some(sender);
1382
1383 while let Some(update) = updates.next().await {
1384 match update {
1385 LoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1386 sender
1387 .take()
1388 .expect("The establishing secure channel update should be received only once")
1389 .send(check_code)
1390 .expect("Bob should be able to send the check code to Alice");
1391 }
1392 LoginProgress::Done => break,
1393 _ => (),
1394 }
1395 }
1396 });
1397 let _alice_task =
1398 spawn(async move { grant_login(alice, receiver, AliceBehaviour::HappyPath).await });
1399 let error = login_bob.await.unwrap_err();
1400
1401 assert_matches!(
1402 error,
1403 QRCodeLoginError::OAuth(DeviceAuthorizationOAuthError::NoDeviceAuthorizationEndpoint)
1404 );
1405 }
1406}