From dfb65d771f4fee849a5c40c27f237cae4ec00e23 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Wed, 14 Jan 2026 04:26:21 +0000 Subject: [PATCH] Add `sso_default_provider_id` option and defaulting behavior. Signed-off-by: Jason Volk --- src/api/client/session/sso.rs | 62 ++++++++++++++++++++++++++++++----- src/core/config/mod.rs | 13 ++++++++ tuwunel-example.toml | 13 ++++++++ 3 files changed, 80 insertions(+), 8 deletions(-) diff --git a/src/api/client/session/sso.rs b/src/api/client/session/sso.rs index eedd87b7..d71eeb8c 100644 --- a/src/api/client/session/sso.rs +++ b/src/api/client/session/sso.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, time::Duration}; +use std::{borrow::Cow, net::IpAddr, time::Duration}; use axum::extract::State; use axum_client_ip::InsecureClientIp; @@ -63,6 +63,10 @@ struct GrantCookie<'a> { static GRANT_SESSION_COOKIE: &str = "tuwunel_grant_session"; +/// # `GET /_matrix/client/v3/login/sso/redirect` +/// +/// A web-based Matrix client should instruct the user’s browser to navigate to +/// this endpoint in order to log in via SSO. #[tracing::instrument( name = "sso_login", level = "debug", @@ -70,16 +74,47 @@ static GRANT_SESSION_COOKIE: &str = "tuwunel_grant_session"; fields(%client), )] pub(crate) async fn sso_login_route( - State(_services): State, + State(services): State, InsecureClientIp(client): InsecureClientIp, - _body: Ruma, + body: Ruma, ) -> Result { - Err!(Request(NotImplemented( - "sso_custom_providers_page has been enabled but this URL has not been overridden with \ - any custom page listing the available providers..." - ))) + if services.config.sso_custom_providers_page { + return Err!(Request(NotImplemented( + "sso_custom_providers_page has been enabled but this URL has not been overridden \ + with any custom page listing the available providers..." + ))); + } + + if services.config.identity_provider.len() > 1 { + return Err!(Config( + "sso_default_provider_id", + "This must be set when using more than one identity provider." + )); + } + + let idp_id = services + .config + .identity_provider + .iter() + .next() + .map(|idp| idp.client_id.clone()) + .unwrap_or_default(); + + let redirect_url = body.body.redirect_url; + + handle_sso_login(&services, &client, idp_id, redirect_url) + .map_ok(|response| sso_login::v3::Response { + location: response.location, + cookie: response.cookie, + }) + .await } +/// # `GET /_matrix/client/v3/login/sso/redirect/{idpId}` +/// +/// This endpoint is the same as /login/sso/redirect, though with an IdP ID from +/// the original identity_providers array to inform the server of which IdP the +/// client/user would like to continue with. #[tracing::instrument( name = "sso_login_with_provider", level = "info", @@ -95,7 +130,18 @@ pub(crate) async fn sso_login_with_provider_route( InsecureClientIp(client): InsecureClientIp, body: Ruma, ) -> Result { - let sso_login_with_provider::v3::Request { idp_id, redirect_url } = body.body; + let idp_id = body.body.idp_id; + let redirect_url = body.body.redirect_url; + + handle_sso_login(&services, &client, idp_id, redirect_url).await +} + +async fn handle_sso_login( + services: &Services, + _client: &IpAddr, + idp_id: String, + redirect_url: String, +) -> Result { let Ok(redirect_url) = redirect_url.parse::() else { return Err!(Request(InvalidParam("Invalid redirect_url"))); }; diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index 13d29f5a..918bdbe5 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -2139,6 +2139,19 @@ pub struct Config { #[serde(default = "default_one_time_key_limit")] pub one_time_key_limit: usize, + /// This option is relevant when more than one identity_provider has been + /// configured and `sso_custom_providers_page` is false. Set this option to + /// the `client_id` of the provider to use for requests to + /// `/_matrix/client/v3/login/sso/redirect` (the url lacks a `client_id`). + /// + /// This *must* be configured for some clients (e.g. fluffychat) to work + /// properly when the above conditions require it. + /// + /// When only one identity_provider is configured that will be used as the + /// default and this does not have to be set. + #[serde(default)] + pub sso_default_provider_id: String, + /// Setting this option to true replaces the list of identity providers on /// the client's login screen with a single button "Sign in with single /// sign-on" linking to the URL `/_matrix/client/v3/login/sso/redirect`. The diff --git a/tuwunel-example.toml b/tuwunel-example.toml index 781b2852..44ace394 100644 --- a/tuwunel-example.toml +++ b/tuwunel-example.toml @@ -1834,6 +1834,19 @@ # #one_time_key_limit = 256 +# This option is relevant when more than one identity_provider has been +# configured and `sso_custom_providers_page` is false. Set this option to +# the `client_id` of the provider to use for requests to +# `/_matrix/client/v3/login/sso/redirect` (the url lacks a `client_id`). +# +# This *must* be configured for some clients (e.g. fluffychat) to work +# properly when the above conditions require it. +# +# When only one identity_provider is configured that will be used as the +# default and this does not have to be set. +# +#sso_default_provider_id = false + # Setting this option to true replaces the list of identity providers on # the client's login screen with a single button "Sign in with single # sign-on" linking to the URL `/_matrix/client/v3/login/sso/redirect`. The