7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 1 | // Copyright 2025 The Matrix.org Foundation C.I.C. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 2 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 4 | // you may not use this file except in compliance with the License. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 5 | // You may obtain a copy of the License at |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 6 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 8 | // |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 9 | // Unless required by applicable law or agreed to in writing, software |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 12 | // See the License for that specific language governing permissions and |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 13 | // limitations under the License. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 14 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 15 | use std::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 16 | collections::HashMap, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 17 | fmt::{self, Debug}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 18 | sync::Arc, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 19 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 20 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 21 | use matrix_sdk::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 22 | Error, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 23 | authentication::oauth::{ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 24 | ClientId, ClientRegistrationData, OAuthError as SdkOAuthError, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 25 | error::OAuthAuthorizationCodeError, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 26 | registration::{ApplicationType, ClientMetadata, Localized, OAuthGrantType}, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 27 | }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 28 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 29 | use ruma::serde::Raw; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 30 | use url::Url; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 31 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 32 | use crate::client::{Client, OAuthPrompt, SlidingSyncVersion}; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 33 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 34 | #[derive(uniffi::Object)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 35 | pub struct HomeserverLoginDetails { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 36 | pub(crate) url: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 37 | pub(crate) sliding_sync_version: SlidingSyncVersion, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 38 | pub(crate) supports_oauth_login: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 39 | pub(crate) supported_oauth_prompts: Vec<OAuthPrompt>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 40 | pub(crate) supports_sso_login: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 41 | pub(crate) supports_password_login: bool, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 42 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 43 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 44 | #[matrix_sdk_ffi_macros::export] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 45 | impl HomeserverLoginDetails { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 46 | /// The URL of the currently configured homeserver. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 47 | pub fn url(&self) -> String { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 48 | self.url.clone() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 49 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 50 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 51 | /// The sliding sync version. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 52 | pub fn sliding_sync_version(&self) -> SlidingSyncVersion { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 53 | self.sliding_sync_version.clone() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 54 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 55 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 56 | /// Whether the current homeserver supports login using OAuth. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 57 | pub fn supports_oauth_login(&self) -> bool { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 58 | self.supports_oauth_login |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 59 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 60 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 61 | /// The prompts advertised by the authentication issuer for use in the login |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 62 | /// URL. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 63 | pub fn supported_oauth_prompts(&self) -> Vec<OAuthPrompt> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 64 | self.supported_oauth_prompts.clone() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 65 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 66 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 67 | /// Whether the current homeserver supports login using legacy SSO. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 68 | pub fn supports_sso_login(&self) -> bool { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 69 | self.supports_sso_login |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 70 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 71 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 72 | /// Whether the current homeserver supports the password login flow. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 73 | pub fn supports_password_login(&self) -> bool { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 74 | self.supports_password_login |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 75 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 76 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 77 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 78 | /// An object encapsulating the SSO login flow |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 79 | #[derive(uniffi::Object)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 80 | pub struct SsoHandler { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 81 | /// The wrapped Client. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 82 | pub(crate) client: Arc<Client>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 83 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 84 | /// The underlying URL for authentication. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 85 | pub(crate) url: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 86 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 87 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 88 | #[matrix_sdk_ffi_macros::export] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 89 | impl SsoHandler { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 90 | /// Returns the URL for starting SSO authentication. The URL should be |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 91 | /// opened in a web view. Once the web view succeeds, call `finish` with |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 92 | /// the callback URL. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 93 | pub fn url(&self) -> String { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 94 | self.url.clone() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 95 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 96 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 97 | /// Completes the SSO login process. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 98 | pub async fn finish(&self, callback_url: String) -> Result<(), SsoError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 99 | let auth = self.client.inner.matrix_auth(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 100 | let url = Url::parse(&callback_url).map_err(|_| SsoError::CallbackUrlInvalid)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 101 | let builder = |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 102 | auth.login_with_sso_callback(url.into()).map_err(|_| SsoError::CallbackUrlInvalid)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 103 | builder.await.map_err(|_| SsoError::LoginWithTokenFailed)?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 104 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 105 | Ok(()) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 106 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 107 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 108 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 109 | impl Debug for SsoHandler { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 110 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 111 | fmt.debug_struct("SsoHandler").field("url", &self.url).finish_non_exhaustive() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 112 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 113 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 114 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 115 | #[derive(Debug, thiserror::Error, uniffi::Error)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 116 | #[uniffi(flat_error)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 117 | pub enum SsoError { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 118 | #[error("The supplied callback URL used to complete SSO is invalid.")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 119 | CallbackUrlInvalid, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 120 | #[error("Logging in with the token from the supplied callback URL failed.")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 121 | LoginWithTokenFailed, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 122 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 123 | #[error("An error occurred: {message}")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 124 | Generic { message: String }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 125 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 126 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 127 | /// The configuration to use when authenticating with OAuth. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 128 | #[derive(uniffi::Record)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 129 | pub struct OAuthConfiguration { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 130 | /// The name of the client that will be shown during OAuth authentication. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 131 | pub client_name: Option<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 132 | /// The redirect URI that will be used when OAuth authentication is |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 133 | /// successful. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 134 | pub redirect_uri: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 135 | /// A URI that contains information about the client. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 136 | pub client_uri: String, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 137 | /// A URI that contains the client's logo. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 138 | pub logo_uri: Option<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 139 | /// A URI that contains the client's terms of service. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 140 | pub tos_uri: Option<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 141 | /// A URI that contains the client's privacy policy. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 142 | pub policy_uri: Option<String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 143 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 144 | /// Pre-configured registrations for use with homeservers that don't support |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 145 | /// dynamic client registration. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 146 | /// |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 147 | /// The keys of the map should be the URLs of the homeservers, but keys |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 148 | /// using `issuer` URLs are also supported. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 149 | pub static_registrations: HashMap<String, String>, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 150 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 151 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 152 | impl OAuthConfiguration { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 153 | pub(crate) fn redirect_uri(&self) -> Result<Url, OAuthError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 154 | Url::parse(&self.redirect_uri).map_err(|_| OAuthError::CallbackUrlInvalid) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 155 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 156 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 157 | pub(crate) fn client_metadata(&self) -> Result<Raw<ClientMetadata>, OAuthError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 158 | let redirect_uri = self.redirect_uri()?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 159 | let client_name = self.client_name.as_ref().map(|n| Localized::new(n.to_owned(), [])); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 160 | let client_uri = self.client_uri.localized_url()?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 161 | let logo_uri = self.logo_uri.localized_url()?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 162 | let policy_uri = self.policy_uri.localized_url()?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 163 | let tos_uri = self.tos_uri.localized_url()?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 164 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 165 | let metadata = ClientMetadata { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 166 | // The server should display the following fields when getting the user's consent. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 167 | client_name, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 168 | logo_uri, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 169 | policy_uri, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 170 | tos_uri, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 171 | ..ClientMetadata::new( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 172 | ApplicationType::Native, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 173 | vec![ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 174 | OAuthGrantType::AuthorizationCode { redirect_uris: vec![redirect_uri] }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 175 | OAuthGrantType::DeviceCode, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 176 | ], |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 177 | client_uri, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 178 | ) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 179 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 180 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 181 | Raw::new(&metadata).map_err(|_| OAuthError::MetadataInvalid) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 182 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 183 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 184 | pub(crate) fn registration_data(&self) -> Result<ClientRegistrationData, OAuthError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 185 | let client_metadata = self.client_metadata()?; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 186 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 187 | let mut registration_data = ClientRegistrationData::new(client_metadata); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 188 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 189 | if !self.static_registrations.is_empty() { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 190 | let static_registrations = self |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 191 | .static_registrations |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 192 | .iter() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 193 | .filter_map(|(issuer, client_id)| { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 194 | let Ok(issuer) = Url::parse(issuer) else { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 195 | tracing::error!("Failed to parse {issuer:?}"); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 196 | return None; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 197 | }; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 198 | Some((issuer, ClientId::new(client_id.clone()))) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 199 | }) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 200 | .collect(); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 201 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 202 | registration_data.static_registrations = Some(static_registrations); |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 203 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 204 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 205 | Ok(registration_data) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 206 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 207 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 208 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 209 | #[derive(Debug, thiserror::Error, uniffi::Error)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 210 | #[uniffi(flat_error)] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 211 | pub enum OAuthError { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 212 | #[error( |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 213 | "The homeserver doesn't provide an authentication issuer in its well-known configuration." |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 214 | )] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 215 | NotSupported, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 216 | #[error("Unable to use OAuth as the supplied client metadata is invalid.")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 217 | MetadataInvalid, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 218 | #[error("The supplied callback URL used to complete OAuth is invalid.")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 219 | CallbackUrlInvalid, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 220 | #[error("The OAuth login was cancelled by the user.")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 221 | Cancelled, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 222 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 223 | #[error("An error occurred: {message}")] |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 224 | Generic { message: String }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 225 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 226 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 227 | impl From<SdkOAuthError> for OAuthError { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 228 | fn from(e: SdkOAuthError) -> OAuthError { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 229 | match e { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 230 | SdkOAuthError::Discovery(error) if error.is_not_supported() => OAuthError::NotSupported, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 231 | SdkOAuthError::AuthorizationCode(OAuthAuthorizationCodeError::RedirectUri(_)) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 232 | | SdkOAuthError::AuthorizationCode(OAuthAuthorizationCodeError::InvalidState) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 233 | OAuthError::CallbackUrlInvalid |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 234 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 235 | SdkOAuthError::AuthorizationCode(OAuthAuthorizationCodeError::Cancelled) => { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 236 | OAuthError::Cancelled |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 237 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 238 | _ => OAuthError::Generic { message: e.to_string() }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 239 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 240 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 241 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 242 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 243 | impl From<Error> for OAuthError { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 244 | fn from(e: Error) -> OAuthError { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 245 | match e { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 246 | Error::OAuth(e) => (*e).into(), |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 247 | _ => OAuthError::Generic { message: e.to_string() }, |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 248 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 249 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 250 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 251 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 252 | /* Helpers */ |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 253 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 254 | trait OptionExt { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 255 | /// Convenience method to convert an `Option<String>` to a URL and returns |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 256 | /// it as a Localized URL. No localization is actually performed. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 257 | fn localized_url(&self) -> Result<Option<Localized<Url>>, OAuthError>; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 258 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 259 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 260 | impl OptionExt for Option<String> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 261 | fn localized_url(&self) -> Result<Option<Localized<Url>>, OAuthError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 262 | self.as_deref().map(StrExt::localized_url).transpose() |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 263 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 264 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 265 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 266 | trait StrExt { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 267 | /// Convenience method to convert a string to a URL and returns it as a |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 268 | /// Localized URL. No localization is actually performed. |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 269 | fn localized_url(&self) -> Result<Localized<Url>, OAuthError>; |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 270 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 271 | |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 272 | impl StrExt for str { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 273 | fn localized_url(&self) -> Result<Localized<Url>, OAuthError> { |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 274 | Ok(Localized::new(Url::parse(self).map_err(|_| OAuthError::MetadataInvalid)?, [])) |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 275 | } |
7ff8adafe9e4 | atagen | 2026-10-22 12:30 UTC | 276 | } |