From 3e5e6efb31e08cd2fbb5ec0a356ba52a19982b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Fri, 27 Feb 2026 17:19:21 +0100 Subject: [PATCH] refactor(sdk): Move UrlOrQuery into utils module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will allow to reuse it outside of the `oauth` module. It can now also be converted from a `QueryString`, for improved compatibility with `LocalServerRedirectHandle`. Signed-off-by: Kévin Commaille --- crates/matrix-sdk/CHANGELOG.md | 3 ++ .../src/authentication/oauth/mod.rs | 33 ++------------- .../src/authentication/oauth/tests.rs | 3 +- crates/matrix-sdk/src/utils/mod.rs | 41 +++++++++++++++++++ examples/oauth_cli/src/main.rs | 11 +++-- 5 files changed, 57 insertions(+), 34 deletions(-) diff --git a/crates/matrix-sdk/CHANGELOG.md b/crates/matrix-sdk/CHANGELOG.md index 65a4a8dff..e653807dc 100644 --- a/crates/matrix-sdk/CHANGELOG.md +++ b/crates/matrix-sdk/CHANGELOG.md @@ -91,6 +91,9 @@ All notable changes to this project will be documented in this file. ### Refactor +- [**breaking**] The `UrlOrQuery` enum was moved from the `authentication::oauth` + module to the `utils` module. It can also be converted from a `QueryString`. + ([#6224](https://github.com/matrix-org/matrix-rust-sdk/pull/6224)) - [**breaking**] `Room::report_content()` no longer takes a `score` argument, because it was removed from the Matrix specification. The `ReportedContentScore` type was removed too. ([#6256](https://github.com/matrix-org/matrix-rust-sdk/pull/6256)) diff --git a/crates/matrix-sdk/src/authentication/oauth/mod.rs b/crates/matrix-sdk/src/authentication/oauth/mod.rs index 37d988d28..4d0ec087a 100644 --- a/crates/matrix-sdk/src/authentication/oauth/mod.rs +++ b/crates/matrix-sdk/src/authentication/oauth/mod.rs @@ -223,7 +223,10 @@ use self::{ registration::{ClientMetadata, ClientRegistrationResponse, register_client}, }; use super::{AuthData, SessionTokens}; -use crate::{Client, HttpError, RefreshTokenError, Result, client::SessionChange, executor::spawn}; +use crate::{ + Client, HttpError, RefreshTokenError, Result, client::SessionChange, executor::spawn, + utils::UrlOrQuery, +}; pub(crate) struct OAuthCtx { /// Lock and state when multiple processes may refresh an OAuth 2.0 session. @@ -1797,31 +1800,3 @@ impl From> for ClientRegistrationData { Self::new(value) } } - -/// A full URL or just the query part of a URL. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum UrlOrQuery { - /// A full URL. - Url(Url), - - /// The query part of a URL. - Query(String), -} - -impl UrlOrQuery { - /// Get the query part of this [`UrlOrQuery`]. - /// - /// If this is a [`Url`], this extracts the query. - pub fn query(&self) -> Option<&str> { - match self { - Self::Url(url) => url.query(), - Self::Query(query) => Some(query), - } - } -} - -impl From for UrlOrQuery { - fn from(value: Url) -> Self { - Self::Url(value) - } -} diff --git a/crates/matrix-sdk/src/authentication/oauth/tests.rs b/crates/matrix-sdk/src/authentication/oauth/tests.rs index 349be5898..574117b72 100644 --- a/crates/matrix-sdk/src/authentication/oauth/tests.rs +++ b/crates/matrix-sdk/src/authentication/oauth/tests.rs @@ -12,7 +12,7 @@ use url::Url; use super::{ AuthorizationCode, AuthorizationError, AuthorizationResponse, OAuth, OAuthAuthorizationData, - OAuthError, RedirectUriQueryParseError, UrlOrQuery, + OAuthError, RedirectUriQueryParseError, }; use crate::{ Client, Error, SessionChange, @@ -28,6 +28,7 @@ use crate::{ }, mocks::MatrixMockServer, }, + utils::UrlOrQuery, }; const REDIRECT_URI_STRING: &str = "http://127.0.0.1:6778/oauth/callback"; diff --git a/crates/matrix-sdk/src/utils/mod.rs b/crates/matrix-sdk/src/utils/mod.rs index d9f8f8a91..a9b787220 100644 --- a/crates/matrix-sdk/src/utils/mod.rs +++ b/crates/matrix-sdk/src/utils/mod.rs @@ -33,10 +33,13 @@ use serde_json::value::{RawValue as RawJsonValue, Value as JsonValue}; use tokio::sync::broadcast; #[cfg(feature = "e2e-encryption")] use tokio_stream::wrappers::{BroadcastStream, errors::BroadcastStreamRecvError}; +use url::Url; #[cfg(feature = "local-server")] pub mod local_server; +#[cfg(feature = "local-server")] +use self::local_server::QueryString; #[cfg(doc)] use crate::Room; @@ -238,6 +241,44 @@ pub fn formatted_body_from( if formatted_body.is_some() { formatted_body } else { body.and_then(FormattedBody::markdown) } } +/// A full URL or just the query part of a URL. +/// +/// This is a convenience type to be able to access the query part of a URL from +/// different sources. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum UrlOrQuery { + /// A full URL. + Url(Url), + + /// The query part of a URL. + Query(String), +} + +impl UrlOrQuery { + /// Get the query part of this [`UrlOrQuery`]. + /// + /// If this is a [`Url`], this extracts the query. + pub fn query(&self) -> Option<&str> { + match self { + Self::Url(url) => url.query(), + Self::Query(query) => Some(query), + } + } +} + +impl From for UrlOrQuery { + fn from(value: Url) -> Self { + Self::Url(value) + } +} + +#[cfg(feature = "local-server")] +impl From for UrlOrQuery { + fn from(value: QueryString) -> Self { + Self::Query(value.0) + } +} + #[cfg(test)] mod test { #[cfg(feature = "markdown")] diff --git a/examples/oauth_cli/src/main.rs b/examples/oauth_cli/src/main.rs index 3ca521cac..accbe0abd 100644 --- a/examples/oauth_cli/src/main.rs +++ b/examples/oauth_cli/src/main.rs @@ -24,7 +24,7 @@ use futures_util::StreamExt; use matrix_sdk::{ Client, ClientBuildError, Result, RoomState, authentication::oauth::{ - ClientId, OAuthAuthorizationData, OAuthError, OAuthSession, UrlOrQuery, UserSession, + ClientId, OAuthAuthorizationData, OAuthError, OAuthSession, UserSession, error::OAuthClientRegistrationError, registration::{ApplicationType, ClientMetadata, Localized, OAuthGrantType}, }, @@ -192,10 +192,13 @@ impl OAuthCli { .build() .await?; - let query_string = - use_auth_url(&url, server_handle).await.map(|query| query.0).unwrap_or_default(); + let Some(query_string) = use_auth_url(&url, server_handle).await else { + println!("Error: failed to login: missing query string on the redirect URL"); + println!("Please try again.\n"); + continue; + }; - match oauth.finish_login(UrlOrQuery::Query(query_string)).await { + match oauth.finish_login(query_string.into()).await { Ok(()) => { let user_id = self.client.user_id().expect("Got a user ID"); println!("Logged in as {user_id}");