diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08849418..630cf331 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,6 +102,7 @@ jobs: - linux / features-markdown - linux / features-socks - linux / features-sso_login + - linux / features-require_auth_for_profile_requests include: - name: linux / features-no-encryption @@ -119,6 +120,9 @@ jobs: - name: linux / features-rustls-tls cargo_args: --no-default-features --features rustls-tls + - name: linux / features-require_auth_for_profile_requests + cargo_args: --no-default-features --features "require_auth_for_profile_requests, native-tls" + - name: linux / features-markdown cargo_args: --features markdown diff --git a/matrix_sdk/Cargo.toml b/matrix_sdk/Cargo.toml index cc254dd3..0ab3b50f 100644 --- a/matrix_sdk/Cargo.toml +++ b/matrix_sdk/Cargo.toml @@ -15,7 +15,7 @@ features = ["docs"] rustdoc-args = ["--cfg", "feature=\"docs\""] [features] -default = ["encryption", "sled_cryptostore", "sled_state_store", "native-tls"] +default = ["encryption", "sled_cryptostore", "sled_state_store", "require_auth_for_profile_requests", "native-tls"] encryption = ["matrix-sdk-base/encryption"] sled_state_store = ["matrix-sdk-base/sled_state_store"] @@ -25,6 +25,7 @@ native-tls = ["reqwest/native-tls"] rustls-tls = ["reqwest/rustls-tls"] socks = ["reqwest/socks"] sso_login = ["warp", "rand", "tokio-stream"] +require_auth_for_profile_requests = [] docs = ["encryption", "sled_cryptostore", "sled_state_store", "sso_login"] diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 2d2e3f57..ada6ced0 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -405,6 +405,7 @@ pub struct RequestConfig { pub(crate) timeout: Duration, pub(crate) retry_limit: Option, pub(crate) retry_timeout: Option, + pub(crate) force_auth: bool, } #[cfg(not(tarpaulin_include))] @@ -425,6 +426,7 @@ impl Default for RequestConfig { timeout: DEFAULT_REQUEST_TIMEOUT, retry_limit: Default::default(), retry_timeout: Default::default(), + force_auth: false, } } } @@ -459,6 +461,14 @@ impl RequestConfig { self.retry_timeout = Some(retry_timeout); self } + + /// Force sending authorization even if the endpoint does not require it. Default is only + /// sending authorization if it is required + #[cfg(feature = "require_auth_for_profile_requests")] + pub(crate) fn force_auth(mut self) -> Self { + self.force_auth = true; + self + } } impl Client { @@ -610,7 +620,14 @@ impl Client { pub async fn avatar_url(&self) -> Result> { let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?; let request = get_avatar_url::Request::new(&user_id); - let response = self.send(request, None).await?; + + #[cfg(not(feature = "require_auth_for_profile_requests"))] + let config = None; + + #[cfg(feature = "require_auth_for_profile_requests")] + let config = Some(RequestConfig::new().force_auth()); + + let response = self.send(request, config).await?; Ok(response.avatar_url) } diff --git a/matrix_sdk/src/error.rs b/matrix_sdk/src/error.rs index bb6ac52b..3fd66c24 100644 --- a/matrix_sdk/src/error.rs +++ b/matrix_sdk/src/error.rs @@ -47,6 +47,10 @@ pub enum HttpError { #[error("the queried endpoint requires authentication but was called before logging in")] AuthenticationRequired, + /// Client tried to force authentication but did not provide an access token. + #[error("tried to force authentication but no access token was provided")] + ForcedAuthenticationWithoutAccessToken, + /// Queried endpoint is not meant for clients. #[error("the queried endpoint is not meant for clients")] NotClientRequest, diff --git a/matrix_sdk/src/http_client.rs b/matrix_sdk/src/http_client.rs index 8377c53a..bd2f0d84 100644 --- a/matrix_sdk/src/http_client.rs +++ b/matrix_sdk/src/http_client.rs @@ -105,30 +105,39 @@ impl HttpClient { session: Arc>>, config: Option, ) -> Result, HttpError> { - let request = { - let read_guard; - let access_token = match Request::METADATA.authentication { - AuthScheme::AccessToken => { - read_guard = session.read().await; - - if let Some(session) = read_guard.as_ref() { - SendAccessToken::IfRequired(session.access_token.as_str()) - } else { - return Err(HttpError::AuthenticationRequired); - } - } - AuthScheme::None => SendAccessToken::None, - _ => return Err(HttpError::NotClientRequest), - }; - - request.try_into_http_request(&self.homeserver.to_string(), access_token)? - }; - let config = match config { Some(config) => config, None => self.request_config, }; + let request = { + let read_guard; + let access_token = if config.force_auth { + read_guard = session.read().await; + if let Some(session) = read_guard.as_ref() { + SendAccessToken::Always(session.access_token.as_str()) + } else { + return Err(HttpError::ForcedAuthenticationWithoutAccessToken); + } + } else { + match Request::METADATA.authentication { + AuthScheme::AccessToken => { + read_guard = session.read().await; + + if let Some(session) = read_guard.as_ref() { + SendAccessToken::IfRequired(session.access_token.as_str()) + } else { + return Err(HttpError::AuthenticationRequired); + } + } + AuthScheme::None => SendAccessToken::None, + _ => return Err(HttpError::NotClientRequest), + } + }; + + request.try_into_http_request(&self.homeserver.to_string(), access_token)? + }; + self.inner.send_request(request, config).await } diff --git a/matrix_sdk/src/lib.rs b/matrix_sdk/src/lib.rs index d77ebd68..419ae7ca 100644 --- a/matrix_sdk/src/lib.rs +++ b/matrix_sdk/src/lib.rs @@ -44,6 +44,9 @@ //! * `markdown`: Support for sending markdown formatted messages. //! * `socks`: Enables SOCKS support in reqwest, the default HTTP client. //! * `sso_login`: Enables SSO login with a local http server. +//! * `require_auth_for_profile_requests`: Whether to send the access token in the authentication +//! header when calling endpoints that retrieve profile data. This matches the synapse +//! configuration `require_auth_for_profile_requests`. Enabled by default. #![deny( missing_debug_implementations,