matrix_sdk/authentication/oauth/qrcode/
mod.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
15//! Types for the QR code login support defined in [MSC4108](https://github.com/matrix-org/matrix-spec-proposals/pull/4108).
16//!
17//! Please note, QR code logins are only supported when using OAuth 2.0 as the
18//! authentication mechanism, native Matrix authentication does not support it.
19//!
20//! This currently only implements the case where the new device is scanning the
21//! QR code. To log in using a QR code, please take a look at the
22//! [`OAuth::login_with_qr_code()`] method.
23
24use as_variant::as_variant;
25pub use matrix_sdk_base::crypto::types::qr_login::{
26    LoginQrCodeDecodeError, QrCodeData, QrCodeMode, QrCodeModeData,
27};
28use matrix_sdk_base::crypto::SecretImportError;
29pub use oauth2::{
30    basic::{BasicErrorResponse, BasicRequestTokenError},
31    ConfigurationError, DeviceCodeErrorResponse, DeviceCodeErrorResponseType, HttpClientError,
32    RequestTokenError, StandardErrorResponse,
33};
34use thiserror::Error;
35use url::Url;
36pub use vodozemac::ecies::{Error as EciesError, MessageDecodeError};
37
38mod login;
39mod messages;
40mod rendezvous_channel;
41mod secure_channel;
42
43pub use self::{
44    login::{LoginProgress, LoginWithQrCode},
45    messages::{LoginFailureReason, LoginProtocolType, QrAuthMessage},
46};
47use super::CrossProcessRefreshLockError;
48#[cfg(doc)]
49use super::OAuth;
50use crate::HttpError;
51
52/// The error type for failures while trying to log in a new device using a QR
53/// code.
54#[derive(Debug, Error)]
55#[cfg_attr(feature = "uniffi", derive(uniffi::Error), uniffi(flat_error))]
56pub enum QRCodeLoginError {
57    /// An error happened while we were communicating with the OAuth 2.0
58    /// authorization server.
59    #[error(transparent)]
60    OAuth(#[from] DeviceAuthorizationOAuthError),
61
62    /// The other device has signaled to us that the login has failed.
63    #[error("The login failed, reason: {reason}")]
64    LoginFailure {
65        /// The reason, as signaled by the other device, for the login failure.
66        reason: LoginFailureReason,
67        /// The homeserver that we attempted to log in to.
68        homeserver: Option<Url>,
69    },
70
71    /// An unexpected message was received from the other device.
72    #[error("We have received an unexpected message, expected: {expected}, got {received:?}")]
73    UnexpectedMessage {
74        /// The message we expected.
75        expected: &'static str,
76        /// The message we received instead.
77        received: QrAuthMessage,
78    },
79
80    /// An error happened while exchanging messages with the other device.
81    #[error(transparent)]
82    SecureChannel(#[from] SecureChannelError),
83
84    /// The cross-process refresh lock failed to be initialized.
85    #[error(transparent)]
86    CrossProcessRefreshLock(#[from] CrossProcessRefreshLockError),
87
88    /// An error happened while we were trying to discover our user and device
89    /// ID, after we have acquired an access token from the OAuth 2.0
90    /// authorization server.
91    #[error(transparent)]
92    UserIdDiscovery(HttpError),
93
94    /// We failed to set the session tokens after we figured out our device and
95    /// user IDs.
96    #[error(transparent)]
97    SessionTokens(crate::Error),
98
99    /// The device keys failed to be uploaded after we successfully logged in.
100    #[error(transparent)]
101    DeviceKeyUpload(crate::Error),
102
103    /// The secrets bundle we received from the existing device failed to be
104    /// imported.
105    #[error(transparent)]
106    SecretImport(#[from] SecretImportError),
107}
108
109/// Error type describing failures in the interaction between the device
110/// attempting to log in and the OAuth 2.0 authorization server.
111#[derive(Debug, Error)]
112pub enum DeviceAuthorizationOAuthError {
113    /// A generic OAuth 2.0 error happened while we were attempting to register
114    /// the device with the OAuth 2.0 authorization server.
115    #[error(transparent)]
116    OAuth(#[from] crate::authentication::oauth::OAuthError),
117
118    /// The OAuth 2.0 server doesn't support the device authorization grant.
119    #[error("OAuth 2.0 server doesn't support the device authorization grant")]
120    NoDeviceAuthorizationEndpoint,
121
122    /// An error happened while we attempted to request a device authorization
123    /// from the OAuth 2.0 authorization server.
124    #[error(transparent)]
125    DeviceAuthorization(#[from] BasicRequestTokenError<HttpClientError<reqwest::Error>>),
126
127    /// An error happened while waiting for the access token to be issued and
128    /// sent to us by the OAuth 2.0 authorization server.
129    #[error(transparent)]
130    RequestToken(
131        #[from] RequestTokenError<HttpClientError<reqwest::Error>, DeviceCodeErrorResponse>,
132    ),
133}
134
135impl DeviceAuthorizationOAuthError {
136    /// If the [`DeviceAuthorizationOAuthError`] is of the
137    /// [`DeviceCodeErrorResponseType`] error variant, return it.
138    pub fn as_request_token_error(&self) -> Option<&DeviceCodeErrorResponseType> {
139        let error = as_variant!(self, DeviceAuthorizationOAuthError::RequestToken)?;
140        let request_token_error = as_variant!(error, RequestTokenError::ServerResponse)?;
141
142        Some(request_token_error.error())
143    }
144}
145
146/// Error type for failures in when receiving or sending messages over the
147/// secure channel.
148#[derive(Debug, Error)]
149pub enum SecureChannelError {
150    /// A message we received over the secure channel was not a valid UTF-8
151    /// encoded string.
152    #[error(transparent)]
153    Utf8(#[from] std::str::Utf8Error),
154
155    /// A message has failed to be decrypted.
156    #[error(transparent)]
157    Ecies(#[from] EciesError),
158
159    /// A received message has failed to be decoded.
160    #[error(transparent)]
161    MessageDecode(#[from] MessageDecodeError),
162
163    /// A message couldn't be deserialized from JSON.
164    #[error(transparent)]
165    Json(#[from] serde_json::Error),
166
167    /// The secure channel failed to be established because it received an
168    /// unexpected message.
169    #[error(
170        "The secure channel setup has received an unexpected message, expected: {expected}, got {received}"
171    )]
172    SecureChannelMessage {
173        /// The secure channel message we expected.
174        expected: &'static str,
175        /// The secure channel message we received instead.
176        received: String,
177    },
178
179    /// The secure channel could not have been established, the check code was
180    /// invalid.
181    #[error("The secure channel could not have been established, the check code was invalid")]
182    InvalidCheckCode,
183
184    /// An error happened in the underlying rendezvous channel.
185    #[error("Error in the rendezvous channel: {0:?}")]
186    RendezvousChannel(#[from] HttpError),
187
188    /// Both devices have advertised the same intent in the login attempt, i.e.
189    /// both sides claim to be a new device.
190    #[error("The secure channel could not have been established, the two devices have the same login intent")]
191    InvalidIntent,
192}