Skip to main content

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: Box::new(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: Box::new(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_impl(&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: Box::new(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
499    use super::*;
500    use crate::{
501        authentication::oauth::qrcode::{
502            messages::LoginProtocolType,
503            secure_channel::{SecureChannel, test::MockedRendezvousServer},
504        },
505        config::RequestConfig,
506        http_client::HttpClient,
507        test_utils::{client::oauth::mock_client_metadata, mocks::MatrixMockServer},
508    };
509
510    enum AliceBehaviour {
511        HappyPath,
512        DeclinedProtocol,
513        UnexpectedMessage,
514        UnexpectedMessageInsteadOfSecrets,
515        RefuseSecrets,
516        LetSessionExpire,
517    }
518
519    /// The possible token responses.
520    enum TokenResponse {
521        Ok,
522        AccessDenied,
523        ExpiredToken,
524    }
525
526    fn secrets_bundle() -> SecretsBundle {
527        let json = json!({
528            "cross_signing": {
529                "master_key": "rTtSv67XGS6k/rg6/yTG/m573cyFTPFRqluFhQY+hSw",
530                "self_signing_key": "4jbPt7jh5D2iyM4U+3IDa+WthgJB87IQN1ATdkau+xk",
531                "user_signing_key": "YkFKtkjcsTxF6UAzIIG/l6Nog/G2RigCRfWj3cjNWeM",
532            },
533        });
534
535        serde_json::from_value(json).expect("We should be able to deserialize a secrets bundle")
536    }
537
538    /// This is most of the code that is required to be the other side, the
539    /// existing device, of the QR login dance.
540    async fn grant_login(
541        alice: SecureChannel,
542        check_code_receiver: tokio::sync::oneshot::Receiver<u8>,
543        behavior: AliceBehaviour,
544    ) {
545        let alice = alice.connect().await.expect("Alice should be able to connect the channel");
546
547        let check_code =
548            check_code_receiver.await.expect("We should receive the check code from bob");
549
550        let mut alice =
551            alice.confirm(check_code).expect("Alice should be able to confirm the secure channel");
552
553        let message = alice
554            .receive_json()
555            .await
556            .expect("Alice should be able to receive the initial message from Bob");
557
558        assert_let!(QrAuthMessage::LoginProtocol { protocol, .. } = message);
559        assert_eq!(protocol, LoginProtocolType::DeviceAuthorizationGrant);
560
561        let message = match behavior {
562            AliceBehaviour::DeclinedProtocol => QrAuthMessage::LoginFailure {
563                reason: LoginFailureReason::UnsupportedProtocol,
564                homeserver: None,
565            },
566            AliceBehaviour::UnexpectedMessage => QrAuthMessage::LoginDeclined,
567            _ => QrAuthMessage::LoginProtocolAccepted,
568        };
569
570        alice.send_json(message).await.unwrap();
571
572        let message: QrAuthMessage = alice.receive_json().await.unwrap();
573        assert_let!(QrAuthMessage::LoginSuccess = message);
574
575        let message = match behavior {
576            AliceBehaviour::UnexpectedMessageInsteadOfSecrets => QrAuthMessage::LoginDeclined,
577            AliceBehaviour::RefuseSecrets => QrAuthMessage::LoginFailure {
578                reason: LoginFailureReason::DeviceNotFound,
579                homeserver: None,
580            },
581            _ => QrAuthMessage::LoginSecrets(secrets_bundle()),
582        };
583
584        alice.send_json(message).await.unwrap();
585    }
586
587    #[async_test]
588    async fn test_qr_login() {
589        let server = MatrixMockServer::new().await;
590        let rendezvous_server =
591            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
592        let (sender, receiver) = tokio::sync::oneshot::channel();
593
594        let oauth_server = server.oauth();
595        oauth_server.mock_server_metadata().ok().expect(1).named("server_metadata").mount().await;
596        oauth_server.mock_registration().ok().expect(1).named("registration").mount().await;
597        oauth_server
598            .mock_device_authorization()
599            .ok()
600            .expect(1)
601            .named("device_authorization")
602            .mount()
603            .await;
604        oauth_server.mock_token().ok().expect(1).named("token").mount().await;
605
606        server.mock_versions().ok().expect(1..).named("versions").mount().await;
607        server.mock_who_am_i().ok().expect(1).named("whoami").mount().await;
608        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
609        server.mock_query_keys().ok().expect(1).named("query_keys").mount().await;
610
611        let client = HttpClient::new(reqwest::Client::new(), Default::default());
612        let alice = SecureChannel::reciprocate(client, &rendezvous_server.homeserver_url)
613            .await
614            .expect("Alice should be able to create a secure channel.");
615
616        assert_let!(
617            QrCodeIntentData::Msc4108 {
618                data: Msc4108IntentData::Reciprocate { server_name },
619                ..
620            } = &alice.qr_code_data().intent_data()
621        );
622
623        let bob = Client::builder()
624            .server_name_or_homeserver_url(server_name)
625            .request_config(RequestConfig::new().disable_retry())
626            .build()
627            .await
628            .expect("We should be able to build the Client object from the URL in the QR code");
629
630        let qr_code = alice.qr_code_data().clone();
631
632        let oauth = bob.oauth();
633        let registration_data = mock_client_metadata().into();
634        let login_bob = oauth.login_with_qr_code(Some(&registration_data)).scan(&qr_code);
635        let mut updates = login_bob.subscribe_to_progress();
636
637        let updates_task = spawn(async move {
638            let mut sender = Some(sender);
639
640            while let Some(update) = updates.next().await {
641                match update {
642                    LoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
643                        sender
644                            .take()
645                            .expect("The establishing secure channel update should be received only once")
646                            .send(check_code)
647                            .expect("Bob should be able to send the check code to Alice");
648                    }
649                    LoginProgress::Done => break,
650                    _ => (),
651                }
652            }
653        });
654        let alice_task =
655            spawn(async { grant_login(alice, receiver, AliceBehaviour::HappyPath).await });
656
657        // Wait for all tasks to finish.
658        login_bob.await.expect("Bob should be able to login");
659        alice_task.await.expect("Alice should have completed it's task successfully");
660        updates_task.await.unwrap();
661
662        assert!(bob.encryption().cross_signing_status().await.unwrap().is_complete());
663        let own_identity =
664            bob.encryption().get_user_identity(bob.user_id().unwrap()).await.unwrap().unwrap();
665
666        assert!(own_identity.is_verified());
667    }
668
669    async fn grant_login_with_generated_qr(
670        alice: &Client,
671        qr_receiver: tokio::sync::oneshot::Receiver<QrCodeData>,
672        cctx_receiver: tokio::sync::oneshot::Receiver<CheckCodeSender>,
673        behavior: AliceBehaviour,
674    ) {
675        let qr_code_data = qr_receiver.await.expect("Alice should receive the QR code");
676
677        let mut channel = EstablishedSecureChannel::from_qr_code(
678            alice.inner.http_client.inner.clone(),
679            &qr_code_data,
680            QrCodeIntent::Reciprocate,
681        )
682        .await
683        .expect("Alice should be able to establish the secure channel");
684
685        trace!("Established the secure channel.");
686
687        // The other side isn't yet sure that it's talking to the right device, show
688        // a check code so they can confirm.
689        let check_code = channel.check_code();
690
691        let check_code_sender =
692            cctx_receiver.await.expect("Alice should receive the CheckCodeSender");
693
694        check_code_sender
695            .send(check_code)
696            .await
697            .expect("Alice should be able to send the check code to Bob");
698
699        // Alice sends m.login.protocols message
700        let message = QrAuthMessage::LoginProtocols {
701            protocols: vec![LoginProtocolType::DeviceAuthorizationGrant],
702            homeserver: alice.homeserver(),
703        };
704        channel
705            .send_json(message)
706            .await
707            .expect("Alice should be able to send the `m.login.protocols` message to Bob");
708
709        // Alice receives m.login.protocol message
710        let message: QrAuthMessage = channel
711            .receive_json()
712            .await
713            .expect("Alice should be able to receive the `m.login.protocol` message from Bob");
714        assert_let!(QrAuthMessage::LoginProtocol { protocol, .. } = message);
715        assert_eq!(protocol, LoginProtocolType::DeviceAuthorizationGrant);
716
717        // Alice sends m.login.protocol_accepted message
718        let message = match behavior {
719            AliceBehaviour::DeclinedProtocol => QrAuthMessage::LoginFailure {
720                reason: LoginFailureReason::UnsupportedProtocol,
721                homeserver: None,
722            },
723            AliceBehaviour::UnexpectedMessage => QrAuthMessage::LoginDeclined,
724            _ => QrAuthMessage::LoginProtocolAccepted,
725        };
726        channel
727            .send_json(message)
728            .await
729            .expect("Alice should be able to send the `m.login.protocol_accepted` message to Bob");
730
731        let message: QrAuthMessage = channel
732            .receive_json()
733            .await
734            .expect("Alice should be able to receive the `m.login.success` message from Bob");
735        assert_let!(QrAuthMessage::LoginSuccess = message);
736
737        // Alice sends m.login.secrets message
738        let message = match behavior {
739            AliceBehaviour::UnexpectedMessageInsteadOfSecrets => QrAuthMessage::LoginDeclined,
740            AliceBehaviour::RefuseSecrets => QrAuthMessage::LoginFailure {
741                reason: LoginFailureReason::DeviceNotFound,
742                homeserver: None,
743            },
744            _ => QrAuthMessage::LoginSecrets(secrets_bundle()),
745        };
746        channel
747            .send_json(message)
748            .await
749            .expect("Alice should be able to send the `m.login.secrets` message to Bob");
750    }
751
752    #[async_test]
753    async fn test_generated_qr_login() {
754        let server = MatrixMockServer::new().await;
755        let rendezvous_server =
756            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
757        let (qr_sender, qr_receiver) = tokio::sync::oneshot::channel();
758        let (cctx_sender, cctx_receiver) = tokio::sync::oneshot::channel();
759
760        let oauth_server = server.oauth();
761        oauth_server.mock_server_metadata().ok().expect(1).named("server_metadata").mount().await;
762        oauth_server.mock_registration().ok().expect(1).named("registration").mount().await;
763        oauth_server
764            .mock_device_authorization()
765            .ok()
766            .expect(1)
767            .named("device_authorization")
768            .mount()
769            .await;
770        oauth_server.mock_token().ok().expect(1).named("token").mount().await;
771
772        server.mock_versions().ok().expect(1..).named("versions").mount().await;
773        server.mock_who_am_i().ok().expect(1).named("whoami").mount().await;
774        server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
775        server.mock_query_keys().ok().expect(1).named("query_keys").mount().await;
776
777        let homeserver_url = rendezvous_server.homeserver_url.clone();
778
779        // Create Alice, the existing client, as a logged-in client. They will scan the
780        // QR code generated by Bob.
781        let alice = server.client_builder().logged_in_with_oauth().build().await;
782        assert!(alice.session_meta().is_some(), "Alice should be logged in");
783
784        // Create Bob, the new client. They will generate the QR code.
785        let bob = Client::builder()
786            .server_name_or_homeserver_url(&homeserver_url)
787            .request_config(RequestConfig::new().disable_retry())
788            .build()
789            .await
790            .expect("Should be able to create a client for Bob");
791
792        let secure_channel = SecureChannel::login(bob.inner.http_client.clone(), &homeserver_url)
793            .await
794            .expect("Bob should be able to create a secure channel");
795
796        assert_matches!(
797            secure_channel.qr_code_data().intent_data(),
798            QrCodeIntentData::Msc4108 { data: Msc4108IntentData::Login, .. }
799        );
800
801        let registration_data = mock_client_metadata().into();
802        let bob_oauth = bob.oauth();
803        let bob_login = bob_oauth.login_with_qr_code(Some(&registration_data)).generate();
804        let mut bob_updates = bob_login.subscribe_to_progress();
805
806        let updates_task = spawn(async move {
807            let mut qr_sender = Some(qr_sender);
808            let mut cctx_sender = Some(cctx_sender);
809
810            while let Some(update) = bob_updates.next().await {
811                match update {
812                    LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrReady(qr)) => {
813                        qr_sender
814                            .take()
815                            .expect("The establishing secure channel update with a qr code should be received only once")
816                            .send(qr)
817                            .expect("Bob should be able to send the qr code code to Alice");
818                    }
819                    LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrScanned(
820                        cctx,
821                    )) => {
822                        cctx_sender
823                            .take()
824                            .expect("The establishing secure channel update with a CheckCodeSender should be received only once")
825                            .send(cctx)
826                            .expect("Bob should be able to send the qr code code to Alice");
827                    }
828                    LoginProgress::Done => break,
829                    _ => (),
830                }
831            }
832        });
833
834        let alice_task = spawn(async move {
835            grant_login_with_generated_qr(
836                &alice,
837                qr_receiver,
838                cctx_receiver,
839                AliceBehaviour::HappyPath,
840            )
841            .await
842        });
843
844        // Wait for all tasks to finish.
845        bob_login.await.expect("Bob should be able to login");
846        alice_task.await.expect("Alice should have completed it's task successfully");
847        updates_task.await.unwrap();
848
849        assert!(bob.encryption().cross_signing_status().await.unwrap().is_complete());
850        let own_identity =
851            bob.encryption().get_user_identity(bob.user_id().unwrap()).await.unwrap().unwrap();
852
853        assert!(own_identity.is_verified());
854    }
855
856    #[async_test]
857    async fn test_generated_qr_login_with_homeserver_swap() {
858        let initial_server = MatrixMockServer::new().await;
859        let rendezvous_server =
860            MockedRendezvousServer::new(initial_server.server(), "abcdEFG12345", Duration::MAX)
861                .await;
862        let (qr_sender, qr_receiver) = tokio::sync::oneshot::channel();
863        let (cctx_sender, cctx_receiver) = tokio::sync::oneshot::channel();
864
865        let login_server = MatrixMockServer::new().await;
866        let oauth_server = login_server.oauth();
867        oauth_server.mock_server_metadata().ok().expect(1).named("server_metadata").mount().await;
868        oauth_server.mock_registration().ok().expect(1).named("registration").mount().await;
869        oauth_server
870            .mock_device_authorization()
871            .ok()
872            .expect(1)
873            .named("device_authorization")
874            .mount()
875            .await;
876        oauth_server.mock_token().ok().expect(1).named("token").mount().await;
877
878        initial_server.mock_versions().ok().expect(1..).named("versions").mount().await;
879
880        login_server.mock_well_known().ok().expect(1).named("well_known").mount().await;
881        login_server.mock_versions().ok().expect(1..).named("versions").mount().await;
882        login_server.mock_who_am_i().ok().expect(1).named("whoami").mount().await;
883        login_server.mock_upload_keys().ok().expect(1).named("upload_keys").mount().await;
884        login_server.mock_query_keys().ok().expect(1).named("query_keys").mount().await;
885
886        let rendezvous_homeserver_url = rendezvous_server.homeserver_url.clone();
887
888        // Create Alice, the existing client, as a logged-in client. They will scan the
889        // QR code generated by Bob.
890        let alice = login_server.client_builder().logged_in_with_oauth().build().await;
891        assert!(alice.session_meta().is_some(), "Alice should be logged in");
892
893        // Create Bob, the new client. They will generate the QR code.
894        let bob = Client::builder()
895            .server_name_or_homeserver_url(&rendezvous_homeserver_url)
896            .request_config(RequestConfig::new().disable_retry())
897            .build()
898            .await
899            .expect("Should be able to create a client for Bob");
900
901        let secure_channel =
902            SecureChannel::login(bob.inner.http_client.clone(), &rendezvous_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        // Alice and Bob should have different homeservers configured at the start.
912        let initial_server_url = initial_server.server().uri().parse().unwrap();
913        assert_eq!(bob.homeserver(), initial_server_url);
914        let login_server_url = login_server.server().uri().parse().unwrap();
915        assert_eq!(alice.homeserver(), login_server_url);
916        assert_ne!(initial_server_url, login_server_url);
917
918        let registration_data = mock_client_metadata().into();
919        let bob_oauth = bob.oauth();
920        let bob_login = bob_oauth.login_with_qr_code(Some(&registration_data)).generate();
921        let mut bob_updates = bob_login.subscribe_to_progress();
922
923        let updates_task = spawn(async move {
924            let mut qr_sender = Some(qr_sender);
925            let mut cctx_sender = Some(cctx_sender);
926
927            while let Some(update) = bob_updates.next().await {
928                match update {
929                    LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrReady(qr)) => {
930                        qr_sender
931                            .take()
932                            .expect("The establishing secure channel update with a qr code should be received only once")
933                            .send(qr)
934                            .expect("Bob should be able to send the qr code code to Alice");
935                    }
936                    LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrScanned(
937                        cctx,
938                    )) => {
939                        cctx_sender
940                            .take()
941                            .expect("The establishing secure channel update with a CheckCodeSender should be received only once")
942                            .send(cctx)
943                            .expect("Bob should be able to send the qr code code to Alice");
944                    }
945                    LoginProgress::Done => break,
946                    _ => (),
947                }
948            }
949        });
950
951        let alice_task = spawn(async move {
952            grant_login_with_generated_qr(
953                &alice,
954                qr_receiver,
955                cctx_receiver,
956                AliceBehaviour::HappyPath,
957            )
958            .await
959        });
960
961        // Wait for all tasks to finish.
962        bob_login.await.expect("Bob should be able to login");
963        alice_task.await.expect("Alice should have completed it's task successfully");
964        updates_task.await.unwrap();
965
966        assert!(bob.encryption().cross_signing_status().await.unwrap().is_complete());
967        let own_identity =
968            bob.encryption().get_user_identity(bob.user_id().unwrap()).await.unwrap().unwrap();
969
970        assert!(own_identity.is_verified());
971
972        // After login, Bob should have switched to the new homeserver.
973        assert_eq!(bob.homeserver(), login_server_url);
974    }
975
976    async fn test_failure(
977        token_response: TokenResponse,
978        alice_behavior: AliceBehaviour,
979    ) -> Result<(), QRCodeLoginError> {
980        let server = MatrixMockServer::new().await;
981        let expiration = match alice_behavior {
982            AliceBehaviour::LetSessionExpire => Duration::from_secs(2),
983            _ => Duration::MAX,
984        };
985        let rendezvous_server =
986            MockedRendezvousServer::new(server.server(), "abcdEFG12345", expiration).await;
987        let (sender, receiver) = tokio::sync::oneshot::channel();
988
989        let oauth_server = server.oauth();
990        let expected_calls = match alice_behavior {
991            AliceBehaviour::LetSessionExpire => 0,
992            _ => 1,
993        };
994        oauth_server
995            .mock_server_metadata()
996            .ok()
997            .expect(expected_calls)
998            .named("server_metadata")
999            .mount()
1000            .await;
1001        oauth_server
1002            .mock_registration()
1003            .ok()
1004            .expect(expected_calls)
1005            .named("registration")
1006            .mount()
1007            .await;
1008        oauth_server
1009            .mock_device_authorization()
1010            .ok()
1011            .expect(expected_calls)
1012            .named("device_authorization")
1013            .mount()
1014            .await;
1015
1016        let token_mock = oauth_server.mock_token();
1017        let token_mock = match token_response {
1018            TokenResponse::Ok => token_mock.ok(),
1019            TokenResponse::AccessDenied => token_mock.access_denied(),
1020            TokenResponse::ExpiredToken => token_mock.expired_token(),
1021        };
1022        token_mock.named("token").mount().await;
1023
1024        server.mock_versions().ok().named("versions").mount().await;
1025        server.mock_who_am_i().ok().named("whoami").mount().await;
1026
1027        let client = HttpClient::new(reqwest::Client::new(), Default::default());
1028        let alice = SecureChannel::reciprocate(client, &rendezvous_server.homeserver_url)
1029            .await
1030            .expect("Alice should be able to create a secure channel.");
1031
1032        assert_let!(
1033            QrCodeIntentData::Msc4108 {
1034                data: Msc4108IntentData::Reciprocate { server_name },
1035                ..
1036            } = &alice.qr_code_data().intent_data()
1037        );
1038
1039        let bob = Client::builder()
1040            .server_name_or_homeserver_url(server_name)
1041            .request_config(RequestConfig::new().disable_retry())
1042            .build()
1043            .await
1044            .expect("We should be able to build the Client object from the URL in the QR code");
1045
1046        let qr_code = alice.qr_code_data().clone();
1047
1048        let oauth = bob.oauth();
1049        let registration_data = mock_client_metadata().into();
1050        let login_bob = oauth.login_with_qr_code(Some(&registration_data)).scan(&qr_code);
1051        let mut updates = login_bob.subscribe_to_progress();
1052
1053        let _updates_task = spawn(async move {
1054            let mut sender = Some(sender);
1055
1056            while let Some(update) = updates.next().await {
1057                match update {
1058                    LoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1059                        sender
1060                            .take()
1061                            .expect("The establishing secure channel update should be received only once")
1062                            .send(check_code)
1063                            .expect("Bob should be able to send the check code to Alice");
1064                    }
1065                    LoginProgress::Done => break,
1066                    _ => (),
1067                }
1068            }
1069        });
1070
1071        if !matches!(alice_behavior, AliceBehaviour::LetSessionExpire) {
1072            let _alice_task =
1073                spawn(async move { grant_login(alice, receiver, alice_behavior).await });
1074        }
1075
1076        login_bob.await
1077    }
1078
1079    async fn test_generated_failure(
1080        token_response: TokenResponse,
1081        alice_behavior: AliceBehaviour,
1082    ) -> Result<(), QRCodeLoginError> {
1083        let server = MatrixMockServer::new().await;
1084        let expiration = match alice_behavior {
1085            AliceBehaviour::LetSessionExpire => Duration::from_secs(2),
1086            _ => Duration::MAX,
1087        };
1088        let rendezvous_server =
1089            MockedRendezvousServer::new(server.server(), "abcdEFG12345", expiration).await;
1090
1091        let (qr_sender, qr_receiver) = tokio::sync::oneshot::channel();
1092        let (cctx_sender, cctx_receiver) = tokio::sync::oneshot::channel();
1093
1094        let oauth_server = server.oauth();
1095        let expected_calls = match alice_behavior {
1096            AliceBehaviour::LetSessionExpire => 0,
1097            _ => 1,
1098        };
1099        oauth_server
1100            .mock_server_metadata()
1101            .ok()
1102            .expect(expected_calls)
1103            .named("server_metadata")
1104            .mount()
1105            .await;
1106        oauth_server
1107            .mock_registration()
1108            .ok()
1109            .expect(expected_calls)
1110            .named("registration")
1111            .mount()
1112            .await;
1113        oauth_server
1114            .mock_device_authorization()
1115            .ok()
1116            .expect(expected_calls)
1117            .named("device_authorization")
1118            .mount()
1119            .await;
1120
1121        let token_mock = oauth_server.mock_token();
1122        let token_mock = match token_response {
1123            TokenResponse::Ok => token_mock.ok(),
1124            TokenResponse::AccessDenied => token_mock.access_denied(),
1125            TokenResponse::ExpiredToken => token_mock.expired_token(),
1126        };
1127        token_mock.named("token").mount().await;
1128
1129        server.mock_versions().ok().named("versions").mount().await;
1130        server.mock_who_am_i().ok().named("whoami").mount().await;
1131
1132        let homeserver_url = rendezvous_server.homeserver_url.clone();
1133
1134        // Create Alice, the existing client, as a logged-in client. They will scan the
1135        // QR code generated by Bob.
1136        let alice = server.client_builder().logged_in_with_oauth().build().await;
1137        assert!(alice.session_meta().is_some(), "Alice should be logged in");
1138
1139        // Create Bob, the new client. They will generate the QR code.
1140        let bob = Client::builder()
1141            .server_name_or_homeserver_url(&homeserver_url)
1142            .request_config(RequestConfig::new().disable_retry())
1143            .build()
1144            .await
1145            .expect("Should be able to create a client for Bob");
1146
1147        let secure_channel = SecureChannel::login(bob.inner.http_client.clone(), &homeserver_url)
1148            .await
1149            .expect("Bob should be able to create a secure channel");
1150
1151        assert_matches!(
1152            secure_channel.qr_code_data().intent_data(),
1153            QrCodeIntentData::Msc4108 { data: Msc4108IntentData::Login, .. }
1154        );
1155
1156        let registration_data = mock_client_metadata().into();
1157        let bob_oauth = bob.oauth();
1158        let bob_login = bob_oauth.login_with_qr_code(Some(&registration_data)).generate();
1159        let mut bob_updates = bob_login.subscribe_to_progress();
1160
1161        let _updates_task = spawn(async move {
1162            let mut qr_sender = Some(qr_sender);
1163            let mut cctx_sender = Some(cctx_sender);
1164
1165            while let Some(update) = bob_updates.next().await {
1166                match update {
1167                    LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrReady(qr)) => {
1168                        qr_sender
1169                            .take()
1170                            .expect("The establishing secure channel update with a qr code should be received only once")
1171                            .send(qr)
1172                            .expect("Bob should be able to send the qr code code to Alice");
1173                    }
1174                    LoginProgress::EstablishingSecureChannel(GeneratedQrProgress::QrScanned(
1175                        cctx,
1176                    )) => {
1177                        cctx_sender
1178                            .take()
1179                            .expect("The establishing secure channel update with a CheckCodeSender should be received only once")
1180                            .send(cctx)
1181                            .expect("Bob should be able to send the qr code code to Alice");
1182                    }
1183                    LoginProgress::Done => break,
1184                    _ => (),
1185                }
1186            }
1187        });
1188
1189        if !matches!(alice_behavior, AliceBehaviour::LetSessionExpire) {
1190            let _alice_task = spawn(async move {
1191                grant_login_with_generated_qr(&alice, qr_receiver, cctx_receiver, alice_behavior)
1192                    .await
1193            });
1194        }
1195
1196        bob_login.await
1197    }
1198
1199    #[async_test]
1200    async fn test_qr_login_refused_access_token() {
1201        let result = test_failure(TokenResponse::AccessDenied, AliceBehaviour::HappyPath).await;
1202
1203        assert_let!(Err(QRCodeLoginError::OAuth(e)) = result);
1204        assert_eq!(
1205            e.as_request_token_error(),
1206            Some(&DeviceCodeErrorResponseType::AccessDenied),
1207            "The server should have told us that access has been denied."
1208        );
1209    }
1210
1211    #[async_test]
1212    async fn test_generated_qr_login_refused_access_token() {
1213        let result =
1214            test_generated_failure(TokenResponse::AccessDenied, AliceBehaviour::HappyPath).await;
1215
1216        assert_let!(Err(QRCodeLoginError::OAuth(e)) = result);
1217        assert_eq!(
1218            e.as_request_token_error(),
1219            Some(&DeviceCodeErrorResponseType::AccessDenied),
1220            "The server should have told us that access has been denied."
1221        );
1222    }
1223
1224    #[async_test]
1225    async fn test_qr_login_expired_token() {
1226        let result = test_failure(TokenResponse::ExpiredToken, AliceBehaviour::HappyPath).await;
1227
1228        assert_let!(Err(QRCodeLoginError::OAuth(e)) = result);
1229        assert_eq!(
1230            e.as_request_token_error(),
1231            Some(&DeviceCodeErrorResponseType::ExpiredToken),
1232            "The server should have told us that access has been denied."
1233        );
1234    }
1235
1236    #[async_test]
1237    async fn test_generated_qr_login_expired_token() {
1238        let result =
1239            test_generated_failure(TokenResponse::ExpiredToken, AliceBehaviour::HappyPath).await;
1240
1241        assert_let!(Err(QRCodeLoginError::OAuth(e)) = result);
1242        assert_eq!(
1243            e.as_request_token_error(),
1244            Some(&DeviceCodeErrorResponseType::ExpiredToken),
1245            "The server should have told us that access has been denied."
1246        );
1247    }
1248
1249    #[async_test]
1250    async fn test_qr_login_declined_protocol() {
1251        let result = test_failure(TokenResponse::Ok, AliceBehaviour::DeclinedProtocol).await;
1252
1253        assert_let!(Err(QRCodeLoginError::LoginFailure { reason, .. }) = result);
1254        assert_eq!(
1255            reason,
1256            LoginFailureReason::UnsupportedProtocol,
1257            "Alice should have told us that the protocol is unsupported."
1258        );
1259    }
1260
1261    #[async_test]
1262    async fn test_generated_qr_login_declined_protocol() {
1263        let result =
1264            test_generated_failure(TokenResponse::Ok, AliceBehaviour::DeclinedProtocol).await;
1265
1266        assert_let!(Err(QRCodeLoginError::LoginFailure { reason, .. }) = result);
1267        assert_eq!(
1268            reason,
1269            LoginFailureReason::UnsupportedProtocol,
1270            "Alice should have told us that the protocol is unsupported."
1271        );
1272    }
1273
1274    #[async_test]
1275    async fn test_qr_login_unexpected_message() {
1276        let result = test_failure(TokenResponse::Ok, AliceBehaviour::UnexpectedMessage).await;
1277
1278        assert_let!(Err(QRCodeLoginError::UnexpectedMessage { expected, .. }) = result);
1279        assert_eq!(expected, "m.login.protocol_accepted");
1280    }
1281
1282    #[async_test]
1283    async fn test_generated_qr_login_unexpected_message() {
1284        let result =
1285            test_generated_failure(TokenResponse::Ok, AliceBehaviour::UnexpectedMessage).await;
1286
1287        assert_let!(Err(QRCodeLoginError::UnexpectedMessage { expected, .. }) = result);
1288        assert_eq!(expected, "m.login.protocol_accepted");
1289    }
1290
1291    #[async_test]
1292    async fn test_qr_login_unexpected_message_instead_of_secrets() {
1293        let result =
1294            test_failure(TokenResponse::Ok, AliceBehaviour::UnexpectedMessageInsteadOfSecrets)
1295                .await;
1296
1297        assert_let!(Err(QRCodeLoginError::UnexpectedMessage { expected, .. }) = result);
1298        assert_eq!(expected, "m.login.secrets");
1299    }
1300
1301    #[async_test]
1302    async fn test_generated_qr_login_unexpected_message_instead_of_secrets() {
1303        let result = test_generated_failure(
1304            TokenResponse::Ok,
1305            AliceBehaviour::UnexpectedMessageInsteadOfSecrets,
1306        )
1307        .await;
1308
1309        assert_let!(Err(QRCodeLoginError::UnexpectedMessage { expected, .. }) = result);
1310        assert_eq!(expected, "m.login.secrets");
1311    }
1312
1313    #[async_test]
1314    async fn test_qr_login_refuse_secrets() {
1315        let result = test_failure(TokenResponse::Ok, AliceBehaviour::RefuseSecrets).await;
1316
1317        assert_let!(Err(QRCodeLoginError::LoginFailure { reason, .. }) = result);
1318        assert_eq!(reason, LoginFailureReason::DeviceNotFound);
1319    }
1320
1321    #[async_test]
1322    async fn test_generated_qr_login_refuse_secrets() {
1323        let result = test_generated_failure(TokenResponse::Ok, AliceBehaviour::RefuseSecrets).await;
1324
1325        assert_let!(Err(QRCodeLoginError::LoginFailure { reason, .. }) = result);
1326        assert_eq!(reason, LoginFailureReason::DeviceNotFound);
1327    }
1328
1329    #[async_test]
1330    async fn test_qr_login_session_expired() {
1331        let result = test_failure(TokenResponse::Ok, AliceBehaviour::LetSessionExpire).await;
1332
1333        assert_matches!(result, Err(QRCodeLoginError::NotFound));
1334    }
1335
1336    #[async_test]
1337    async fn test_generated_qr_login_session_expired() {
1338        let result =
1339            test_generated_failure(TokenResponse::Ok, AliceBehaviour::LetSessionExpire).await;
1340
1341        assert_matches!(result, Err(QRCodeLoginError::NotFound));
1342    }
1343
1344    #[async_test]
1345    async fn test_device_authorization_endpoint_missing() {
1346        let server = MatrixMockServer::new().await;
1347        let rendezvous_server =
1348            MockedRendezvousServer::new(server.server(), "abcdEFG12345", Duration::MAX).await;
1349        let (sender, receiver) = tokio::sync::oneshot::channel();
1350
1351        let oauth_server = server.oauth();
1352        oauth_server
1353            .mock_server_metadata()
1354            .ok_without_device_authorization()
1355            .expect(1)
1356            .named("server_metadata")
1357            .mount()
1358            .await;
1359        oauth_server.mock_registration().ok().expect(1).named("registration").mount().await;
1360
1361        server.mock_versions().ok().named("versions").mount().await;
1362        server.mock_who_am_i().ok().named("whoami").mount().await;
1363
1364        let client = HttpClient::new(reqwest::Client::new(), Default::default());
1365        let alice = SecureChannel::reciprocate(client, &rendezvous_server.homeserver_url)
1366            .await
1367            .expect("Alice should be able to create a secure channel.");
1368
1369        assert_let!(
1370            QrCodeIntentData::Msc4108 {
1371                data: Msc4108IntentData::Reciprocate { server_name },
1372                ..
1373            } = &alice.qr_code_data().intent_data()
1374        );
1375
1376        let bob = Client::builder()
1377            .server_name_or_homeserver_url(server_name)
1378            .request_config(RequestConfig::new().disable_retry())
1379            .build()
1380            .await
1381            .expect("We should be able to build the Client object from the URL in the QR code");
1382
1383        let qr_code = alice.qr_code_data().clone();
1384
1385        let oauth = bob.oauth();
1386        let registration_data = mock_client_metadata().into();
1387        let login_bob = oauth.login_with_qr_code(Some(&registration_data)).scan(&qr_code);
1388        let mut updates = login_bob.subscribe_to_progress();
1389
1390        let _updates_task = spawn(async move {
1391            let mut sender = Some(sender);
1392
1393            while let Some(update) = updates.next().await {
1394                match update {
1395                    LoginProgress::EstablishingSecureChannel(QrProgress { check_code }) => {
1396                        sender
1397                                .take()
1398                                .expect("The establishing secure channel update should be received only once")
1399                                .send(check_code)
1400                                .expect("Bob should be able to send the check code to Alice");
1401                    }
1402                    LoginProgress::Done => break,
1403                    _ => (),
1404                }
1405            }
1406        });
1407        let _alice_task =
1408            spawn(async move { grant_login(alice, receiver, AliceBehaviour::HappyPath).await });
1409        let error = login_bob.await.unwrap_err();
1410
1411        assert_matches!(
1412            error,
1413            QRCodeLoginError::OAuth(DeviceAuthorizationOAuthError::NoDeviceAuthorizationEndpoint)
1414        );
1415    }
1416}