refactor(sdk): Move UrlOrQuery into utils module

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 <zecakeh@tedomum.fr>
This commit is contained in:
Kévin Commaille
2026-02-27 17:19:21 +01:00
committed by Damir Jelić
parent 5902a857d9
commit 3e5e6efb31
5 changed files with 57 additions and 34 deletions

View File

@@ -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))

View File

@@ -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<Raw<ClientMetadata>> 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<Url> for UrlOrQuery {
fn from(value: Url) -> Self {
Self::Url(value)
}
}

View File

@@ -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";

View File

@@ -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<Url> for UrlOrQuery {
fn from(value: Url) -> Self {
Self::Url(value)
}
}
#[cfg(feature = "local-server")]
impl From<QueryString> for UrlOrQuery {
fn from(value: QueryString) -> Self {
Self::Query(value.0)
}
}
#[cfg(test)]
mod test {
#[cfg(feature = "markdown")]

View File

@@ -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}");