use dioxus::prelude::*;
use crate::app::Route;
use crate::state::app_state::{AppState, AuthStatus};
#[component]
pub fn SsoLoginButton(homeserver: String) -> Element {
let mut state = use_context::<Signal<AppState>>();
let navigator = use_navigator();
let mut is_loading = use_signal(|| false);
let mut error_message = use_signal(|| Option::<String>::None);
let on_click = move |_| {
is_loading.set(true);
error_message.set(None);
let hs = homeserver.clone();
spawn(async move {
match start_sso_login(&hs).await {
Ok(client) => {
tracing::info!("SSO login successful");
{
let mut w = state.write();
w.auth_status = AuthStatus::LoggedIn;
w.client = Some(client);
}
navigator.push(Route::Home {});
}
Err(e) => {
tracing::error!("SSO login failed: {e}");
error_message.set(Some(e));
is_loading.set(false);
}
}
});
};
rsx! {
div {
class: "sso-login",
if let Some(ref err) = *error_message.read() {
div {
class: "sso-login__error",
"{err}"
}
}
button {
class: "sso-login-button",
onclick: on_click,
disabled: *is_loading.read(),
if *is_loading.read() {
"Opening browser..."
} else {
"Sign in with SSO"
}
}
}
}
}
async fn start_sso_login(homeserver: &str) -> Result<matrix_sdk::Client, String> {
let client = crate::client::build_client(homeserver)
.await
.map_err(|e| format!("Failed to connect: {e}"))?;
let login_types = client
.matrix_auth()
.get_login_types()
.await
.map_err(|e| format!("Failed to get login types: {e}"))?;
let has_sso = login_types.flows.iter().any(|flow| {
matches!(
flow,
matrix_sdk::ruma::api::client::session::get_login_types::v3::LoginType::Sso(_)
)
});
if !has_sso {
return Err("This homeserver does not support SSO login".to_string());
}
let sso_result = client
.matrix_auth()
.login_sso(|url| async move {
tracing::info!("Opening SSO URL: {url}");
#[cfg(target_os = "windows")]
{
let _ = std::process::Command::new("cmd")
.args(["/C", "start", "", &url])
.spawn();
}
#[cfg(target_os = "macos")]
{
let _ = std::process::Command::new("open").arg(&url).spawn();
}
#[cfg(target_os = "linux")]
{
let _ = std::process::Command::new("xdg-open").arg(&url).spawn();
}
Ok(())
})
.initial_device_display_name("Netrix")
.await;
match sso_result {
Ok(_) => {
if let Some(session) = client.matrix_auth().session() {
let session_data = crate::persistence::matrix_state::SessionData {
homeserver_url: homeserver.to_string(),
user_id: session.meta.user_id.to_string(),
device_id: session.meta.device_id.to_string(),
access_token: session.tokens.access_token.clone(),
};
if let Err(e) = crate::persistence::matrix_state::save_session(&session_data).await {
tracing::error!("Failed to save SSO session: {e}");
}
}
Ok(client)
}
Err(e) => Err(format!("SSO login failed: {e}")),
}
}