From 3ab20f1f412cebd24149f50eb89fae1194ce4911 Mon Sep 17 00:00:00 2001 From: Zaid Marji Date: Tue, 2 Jun 2026 17:43:12 +0300 Subject: [PATCH] Centralize SSO_ONLY enforcement (deny-by-default) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the per-arm `if sso_enabled && sso_only` guard in `login()` with a `check_sso_only(grant_type)` helper called once before the grant dispatch. Mirrors upstream Bitwarden's `SsoRequestValidator`: under SSO_ONLY every grant is rejected unless explicitly whitelisted (`authorization_code`, `client_credentials`, `refresh_token`). A future grant cannot silently bypass SSO — every new `grant_type` must be added to the explicit whitelist to be allowed under SSO_ONLY. --- src/api/identity.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/api/identity.rs b/src/api/identity.rs index 4334fd5c..e99c14b4 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -62,6 +62,18 @@ pub fn routes() -> Vec { ] } +/// Deny-by-default SSO gate (mirrors Bitwarden's `SsoRequestValidator`): +/// non-exempt grants are rejected under `SSO_ONLY`. +fn check_sso_only(grant_type: &str) -> EmptyResult { + if !CONFIG.sso_enabled() || !CONFIG.sso_only() { + return Ok(()); + } + match grant_type { + "authorization_code" | "client_credentials" | "refresh_token" => Ok(()), + _ => err!("SSO sign-in is required"), + } +} + #[post("/connect/token", data = "")] async fn login( data: Form, @@ -71,6 +83,8 @@ async fn login( ) -> JsonResult { let data: ConnectData = data.into_inner(); + check_sso_only(data.grant_type.as_ref())?; + let mut user_id: Option = None; let login_result = match data.grant_type.as_ref() { @@ -78,7 +92,6 @@ async fn login( check_is_some(data.refresh_token.as_ref(), "refresh_token cannot be blank")?; refresh_login(data, &conn, &client_header.ip).await } - "password" | "webauthn" if CONFIG.sso_enabled() && CONFIG.sso_only() => err!("SSO sign-in is required"), "password" => { check_is_some(data.client_id.as_ref(), "client_id cannot be blank")?; check_is_some(data.password.as_ref(), "password cannot be blank")?;