matrix_sdk/authentication/oauth/qrcode/
login.rs

1// Copyright 2024 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::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    // Register the client with the OAuth 2.0 authorization server.
67    trace!("Registering the client with the OAuth 2.0 authorization server.");
68    let server_metadata = register_client(&oauth, registration_data).await?;
69
70    // We want to use the Curve25519 public key for the device ID, so let's generate
71    // a new vodozemac `Account` now.
72    let account = vodozemac::olm::Account::new();
73    let public_key = account.identity_keys().curve25519;
74    let device_id = public_key;
75
76    // Let's tell the OAuth 2.0 authorization server that we want to log in using
77    // the device authorization grant described in [RFC8628](https://datatracker.ietf.org/doc/html/rfc8628).
78    trace!("Requesting device authorization.");
79    let auth_grant_response =
80        request_device_authorization(&oauth, &server_metadata, device_id).await?;
81
82    // Now we need to inform the other device of the login protocols we picked and
83    // the URL they should use to log us in.
84    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    // Let's see if the other device agreed to our proposed protocols.
90    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    // The OAuth 2.0 authorization server may or may not show this user code to
106    // double check that we're talking to the right server. Let us display this, so
107    // the other device can double check this as well.
108    let user_code = auth_grant_response.user_code();
109    state.set(LoginProgress::WaitingForToken { user_code: user_code.secret().to_owned() });
110
111    // Let's now wait for the access token to be provided to use by the OAuth 2.0
112    // authorization server.
113    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 we received an error, and it's one of the ones we should report to the
116        // other side, do so now.
117        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    // We only received an access token from the OAuth 2.0 authorization server, we
138    // have no clue who we are, so we need to figure out our user ID
139    // now. TODO: This snippet is almost the same as the
140    // OAuth::finish_login_method(), why is that method even a public
141    // method and not called as part of the set session tokens method.
142    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    // Tell the existing device that we're logged in.
162    trace!("Telling the existing device that we successfully logged in.");
163    let message = QrAuthMessage::LoginSuccess;
164    channel.send_json(&message).await?;
165
166    // Let's wait for the secrets bundle to be sent to us, otherwise we won't be a
167    // fully E2EE enabled device.
168    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    // Import the secrets bundle, this will allow us to sign the device keys with
185    // the master key when we upload them.
186    client.encryption().import_secrets_bundle(&bundle).await?;
187
188    // Upload the device keys, this will ensure that other devices see us as a fully
189    // verified device ass soon as this method returns.
190    client
191        .encryption()
192        .ensure_device_keys_upload()
193        .await
194        .map_err(QRCodeLoginError::DeviceKeyUpload)?;
195
196    // Run and wait for the E2EE initialization tasks, this will ensure that we
197    // ourselves see us as verified and the recovery/backup states will
198    // be known. If we did receive all the secrets in the secrets
199    // bundle, then backups will be enabled after this step as well.
200    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    // Tell our listener that we're done.
206    state.set(LoginProgress::Done);
207
208    // And indeed, we are done with the login.
209    Ok(())
210}
211
212/// Register the client with the OAuth 2.0 authorization server.
213///
214/// Returns the authorization server metadata.
215async 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/// Type telling us about the progress of the QR code login.
246#[derive(Clone, Debug, Default)]
247pub enum LoginProgress<Q> {
248    /// We're just starting up, this is the default and initial state.
249    #[default]
250    Starting,
251    /// We have established the secure channel, but need to exchange the
252    /// [`CheckCode`] so the channel can be verified to indeed be secure.
253    EstablishingSecureChannel(Q),
254    /// We're waiting for the OAuth 2.0 authorization server to give us the
255    /// access token. This will only happen if the other device allows the
256    /// OAuth 2.0 authorization server to do so.
257    WaitingForToken {
258        /// The user code the OAuth 2.0 authorization server has given us, the
259        /// OAuth 2.0 authorization server might ask the other device to
260        /// enter this code.
261        user_code: String,
262    },
263    /// We are syncing secrets.
264    SyncingSecrets,
265    /// The login process has completed.
266    Done,
267}
268
269/// Named future for logging in by scanning a QR code with the
270/// [`OAuth::login_with_qr_code()`] method.
271#[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    /// Subscribe to the progress of QR code login.
281    ///
282    /// It's usually necessary to subscribe to this to let the existing device
283    /// know about the [`CheckCode`] which is used to verify that the two
284    /// devices are communicating in a secure manner.
285    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            // Before we get here, the other device has created a new rendezvous session
297            // and presented a QR code which this device has scanned.
298            // -- MSC4108 Secure channel setup steps 1-3
299
300            // First things first, establish the secure channel. Since we're the one that
301            // scanned the QR code, we're certain that the secure channel is
302            // secure, under the assumption that we didn't scan the wrong QR code.
303            // -- MSC4108 Secure channel setup steps 3-5
304            let channel = self.establish_secure_channel().await?;
305
306            trace!("Established the secure channel.");
307
308            // The other side isn't yet sure that it's talking to the right device, show
309            // a check code so they can confirm.
310            // -- MSC4108 Secure channel setup step 6
311            let check_code = channel.check_code().to_owned();
312            self.state.set(LoginProgress::EstablishingSecureChannel(QrProgress { check_code }));
313
314            // The user now enters the checkcode on the other device which verifies it
315            // and will only facilitate the login if the code matches.
316            // -- MSC4108 Secure channel setup step 7
317
318            // Now attempt to finish the login.
319            // -- MSC4108 OAuth 2.0 login all steps
320            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/// Named future for logging in by generating a QR code with the
351/// [`OAuth::login_with_qr_code()`] method.
352#[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    /// Subscribe to the progress of QR code login.
361    ///
362    /// It's necessary to subscribe to this to show the QR code to the existing
363    /// device so it can send the check code back to this device.
364    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            // Establish and verify the secure channel.
378            // -- MSC4108 Secure channel setup all steps
379            let mut channel = self.establish_secure_channel().await?;
380
381            trace!("Established the secure channel.");
382
383            // Wait for the other device to send us the m.login.protocols message
384            // so that we can discover the homeserver to use for logging in.
385            // -- MSC4108 OAuth 2.0 login step 1
386            let message = channel.receive_json().await?;
387
388            // Verify that the device authorization grant is supported and extract
389            // the homeserver URL.
390            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            // Change the login homeserver if it is different from the server hosting the
419            // secure channel.
420            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            // Proceed with logging in.
428            // -- MSC4108 OAuth 2.0 login remaining steps
429            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        // Create a new ephemeral key pair and a rendezvous session to request a login
448        // with.
449        // -- MSC4108 Secure channel setup steps 1 & 2
450        let secure_channel = SecureChannel::login(http_client, &self.client.homeserver()).await?;
451
452        // Extract the QR code data and emit a progress update so that the caller can
453        // present the QR code for scanning by the other device.
454        // -- MSC4108 Secure channel setup step 3
455        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        // Wait for the secure channel to connect. The other device now needs to scan
462        // the QR code and send us the LoginInitiateMessage which we respond to
463        // with the LoginOkMessage. -- MSC4108 step 4 & 5
464        let channel = secure_channel.connect().await?;
465
466        // The other device now verifies our message, computes the checkcode and
467        // displays it. We emit a progress update to let the caller prompt the
468        // user to enter the checkcode and feed it back to us.
469        // -- MSC4108 Secure channel setup step 6
470        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        // Retrieve the entered checkcode and verify it to confirm that the channel is
477        // actually secure.
478        // -- MSC4108 Secure channel setup step 7
479        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    /// The possible token responses.
521    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    /// This is most of the code that is required to be the other side, the
540    /// existing device, of the QR login dance.
541    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(&registration_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        // Wait for all tasks to finish.
660        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        // The other side isn't yet sure that it's talking to the right device, show
690        // a check code so they can confirm.
691        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        // Alice sends m.login.protocols message
702        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        // Alice receives m.login.protocol message
712        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        // Alice sends m.login.protocol_accepted message
720        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        // Alice sends m.login.secrets message
740        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        // Create Alice, the existing client, as a logged-in client. They will scan the
782        // QR code generated by Bob.
783        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        // Create Bob, the new client. They will generate the QR code.
787        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(&registration_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        // Wait for all tasks to finish.
847        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        // Create Alice, the existing client, as a logged-in client. They will scan the
890        // QR code generated by Bob.
891        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        // Create Bob, the new client. They will generate the QR code.
895        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(&registration_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        // Wait for all tasks to finish.
955        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(&registration_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        // Create Alice, the existing client, as a logged-in client. They will scan the
1125        // QR code generated by Bob.
1126        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        // Create Bob, the new client. They will generate the QR code.
1130        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(&registration_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(&registration_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}