diff --git a/src/api/client/session/mod.rs b/src/api/client/session/mod.rs index 281ac618..97fd275d 100644 --- a/src/api/client/session/mod.rs +++ b/src/api/client/session/mod.rs @@ -22,7 +22,10 @@ use ruma::api::client::session::{ v3::{DiscoveryInfo, HomeserverInfo, LoginInfo}, }, }; -use tuwunel_core::{Err, Result, info, utils::stream::ReadyExt}; +use tuwunel_core::{ + Err, Result, info, + utils::{BoolExt, stream::ReadyExt}, +}; use tuwunel_service::users::device::generate_refresh_token; use self::{ldap::ldap_login, password::password_login}; @@ -45,28 +48,45 @@ pub(crate) async fn get_login_types_route( InsecureClientIp(client): InsecureClientIp, _body: Ruma, ) -> Result { - Ok(get_login_types::v3::Response::new(vec![ - LoginType::Password(PasswordLoginType::default()), + let get_login_token = services.config.login_via_existing_session; + + let identity_providers = services + .config + .sso_custom_providers_page + .is_false() + .then(|| services.config.identity_provider.iter()) + .into_iter() + .flatten() + .cloned() + .map(|config| IdentityProvider { + id: config.id().to_owned(), + brand: Some(config.brand.clone().into()), + icon: config.icon, + name: config.name.unwrap_or(config.brand), + }) + .collect(); + + let flows = [ LoginType::ApplicationService(ApplicationServiceLoginType::default()), LoginType::Jwt(JwtLoginType::default()), - LoginType::Token(TokenLoginType { - get_login_token: services.config.login_via_existing_session, - }), - LoginType::Sso(SsoLoginType { - identity_providers: services - .config - .identity_provider - .iter() - .cloned() - .map(|config| IdentityProvider { - id: config.id().to_owned(), - brand: Some(config.brand.clone().into()), - icon: config.icon, - name: config.name.unwrap_or(config.brand), - }) - .collect(), - }), - ])) + LoginType::Password(PasswordLoginType::default()), + LoginType::Sso(SsoLoginType { identity_providers }), + LoginType::Token(TokenLoginType { get_login_token }), + ]; + + Ok(get_login_types::v3::Response { + flows: flows + .into_iter() + .filter(|login_type| match login_type { + | LoginType::Sso(SsoLoginType { identity_providers }) + if !services.config.sso_custom_providers_page + && identity_providers.is_empty() => + false, + + | _ => true, + }) + .collect(), + }) } /// # `POST /_matrix/client/v3/login` diff --git a/src/api/client/session/sso.rs b/src/api/client/session/sso.rs index 736a7fb1..eedd87b7 100644 --- a/src/api/client/session/sso.rs +++ b/src/api/client/session/sso.rs @@ -75,7 +75,8 @@ pub(crate) async fn sso_login_route( _body: Ruma, ) -> Result { Err!(Request(NotImplemented( - "SSO login without specific provider has not been implemented." + "sso_custom_providers_page has been enabled but this URL has not been overridden with \ + any custom page listing the available providers..." ))) } diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index 2c107a5e..c210d55c 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -2139,6 +2139,23 @@ pub struct Config { #[serde(default = "default_one_time_key_limit")] pub one_time_key_limit: usize, + /// 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 + /// deployment is expected to intercept this URL with their reverse-proxy to + /// provide a custom webpage listing providers; each entry linking or + /// redirecting back to one of the configured identity providers at + /// /_matrix/client/v3/login/sso/redirect/`. + /// + /// This option defaults to false, allowing the client to generate the list + /// of providers or hide all SSO-related options when none configured. + #[serde(default)] + pub sso_custom_providers_page: bool, + + /// Under development; do not enable. + #[serde(default)] + pub sso_aware_preferred: bool, + // external structure; separate section #[serde(default)] pub blurhashing: BlurhashConfig, @@ -2159,9 +2176,6 @@ pub struct Config { #[serde(default)] pub identity_provider: HashSet, - #[serde(default)] - pub sso_aware_preferred: bool, - #[serde(flatten)] #[allow(clippy::zero_sized_map_values)] // this is a catchall, the map shouldn't be zero at runtime diff --git a/tuwunel-example.toml b/tuwunel-example.toml index 0bd58ae8..9bd84b6e 100644 --- a/tuwunel-example.toml +++ b/tuwunel-example.toml @@ -1834,7 +1834,20 @@ # #one_time_key_limit = 256 -# This item is undocumented. Please contribute documentation for it. +# 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 +# deployment is expected to intercept this URL with their reverse-proxy to +# provide a custom webpage listing providers; each entry linking or +# redirecting back to one of the configured identity providers at +# /_matrix/client/v3/login/sso/redirect/`. +# +# This option defaults to false, allowing the client to generate the list +# of providers or hide all SSO-related options when none configured. +# +#sso_custom_providers_page = false + +# Under development; do not enable. # #sso_aware_preferred = false