From 4acd136121d282478a7e3a538204aa7277872c99 Mon Sep 17 00:00:00 2001 From: kittygaming99 <54167086+kittygaming99@users.noreply.github.com> Date: Fri, 7 Aug 2026 12:02:33 +1000 Subject: [PATCH 1/6] Add sso_name_claim to configuration options --- src/config.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config.rs b/src/config.rs index d5b50146..67dc83ea 100644 --- a/src/config.rs +++ b/src/config.rs @@ -831,6 +831,8 @@ make_config! { sso_scopes: String, true, def, "email profile".to_owned(); /// Authorization request extra parameters sso_authorize_extra_params: String, true, def, String::new(); + /// SSO name claim |> The OIDC claim to use for the user's display name. Falls back to `name` and then `preferred_username`. + sso_name_claim: String, true, def, "name".to_string(); /// Use PKCE during Authorization flow sso_pkce: bool, true, def, true; /// Regex for additional trusted Id token audience |> By default only the client_id is trusted. From 75798c46ce88acdf8a40f33d4f2c35e9ab0e2a28 Mon Sep 17 00:00:00 2001 From: kittygaming99 <54167086+kittygaming99@users.noreply.github.com> Date: Fri, 7 Aug 2026 12:14:31 +1000 Subject: [PATCH 2/6] Refactor user name extraction from claims --- src/sso.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/sso.rs b/src/sso.rs index 01fbd906..6e0bbd5e 100644 --- a/src/sso.rs +++ b/src/sso.rs @@ -288,7 +288,21 @@ pub async fn exchange_code( let email_verified = id_claims.email_verified().or(user_info.email_verified()); - let user_name = id_claims.preferred_username().or(user_info.preferred_username()).map(|un| un.to_string()); + let configured_claim = CONFIG.sso_name_claim(); + + let extract_claim = |claims: &serde_json::Value, claim_key: &str| -> Option { + claims.get(claim_key).and_then(|v| v.as_str()).map(|s| s.to_string()) + }; + + let id_claims_json = serde_json::to_value(&id_claims).unwrap_or_default(); + let user_info_json = serde_json::to_value(&user_info).unwrap_or_default(); + + let user_name = extract_claim(&id_claims_json, &configured_claim) + .or_else(|| extract_claim(&user_info_json, &configured_claim)) + .or_else(|| extract_claim(&id_claims_json, "name")) + .or_else(|| extract_claim(&user_info_json, "name")) + .or_else(|| id_claims.preferred_username().map(|n| n.to_string())) + .or_else(|| user_info.preferred_username().map(|n| n.to_string())); let refresh_token = token_response.refresh_token().map(openidconnect::RefreshToken::secret); if refresh_token.is_none() && CONFIG.sso_scopes_vec().contains(&"offline_access".to_owned()) { From 0db744bd2f70fd63bd23a3763db4221cdc79c4a0 Mon Sep 17 00:00:00 2001 From: kittygaming99 <54167086+kittygaming99@users.noreply.github.com> Date: Fri, 7 Aug 2026 12:27:32 +1000 Subject: [PATCH 3/6] Simplify claim extraction logic Removed redundant extraction of 'name' claim from user info. The admin may wish to fall back to preferred_username, even if name exists --- src/sso.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sso.rs b/src/sso.rs index 6e0bbd5e..9f9a8ab5 100644 --- a/src/sso.rs +++ b/src/sso.rs @@ -299,8 +299,6 @@ pub async fn exchange_code( let user_name = extract_claim(&id_claims_json, &configured_claim) .or_else(|| extract_claim(&user_info_json, &configured_claim)) - .or_else(|| extract_claim(&id_claims_json, "name")) - .or_else(|| extract_claim(&user_info_json, "name")) .or_else(|| id_claims.preferred_username().map(|n| n.to_string())) .or_else(|| user_info.preferred_username().map(|n| n.to_string())); From 8e29449fe6200183c580ffdfb4233a862634da75 Mon Sep 17 00:00:00 2001 From: kittygaming99 <54167086+kittygaming99@users.noreply.github.com> Date: Fri, 7 Aug 2026 12:28:58 +1000 Subject: [PATCH 4/6] Update SSO name claim description --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 67dc83ea..3288520e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -831,7 +831,7 @@ make_config! { sso_scopes: String, true, def, "email profile".to_owned(); /// Authorization request extra parameters sso_authorize_extra_params: String, true, def, String::new(); - /// SSO name claim |> The OIDC claim to use for the user's display name. Falls back to `name` and then `preferred_username`. + /// SSO name claim |> The OIDC claim to use for the user's display name. Falls back to `preferred_username`. sso_name_claim: String, true, def, "name".to_string(); /// Use PKCE during Authorization flow sso_pkce: bool, true, def, true; From 8b3f6967bcb027adc86fad25796c3b2c197f2ec8 Mon Sep 17 00:00:00 2001 From: kittygaming99 <54167086+kittygaming99@users.noreply.github.com> Date: Fri, 2 Oct 2026 01:38:56 +1000 Subject: [PATCH 5/6] Update default value for sso_name_claim Change default value of sso_name_claim to an empty string. --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 9e1f09a7..7e68c622 100644 --- a/src/config.rs +++ b/src/config.rs @@ -834,7 +834,7 @@ make_config! { /// Authorization request extra parameters sso_authorize_extra_params: String, true, def, String::new(); /// SSO name claim |> The OIDC claim to use for the user's display name. Falls back to `preferred_username`. - sso_name_claim: String, true, def, "name".to_string(); + sso_name_claim: String, true, def, String::new(); /// Use PKCE during Authorization flow sso_pkce: bool, true, def, true; /// Regex for additional trusted Id token audience |> By default only the client_id is trusted. From 4c4ac81684f5fa4e4726bc384e10856bf8aa192d Mon Sep 17 00:00:00 2001 From: kittygaming99 <54167086+kittygaming99@users.noreply.github.com> Date: Fri, 2 Oct 2026 01:54:33 +1000 Subject: [PATCH 6/6] Refactor user name extraction logic Building on the suggestions by Timshel and adding checks for blank display names. --- src/sso.rs | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/sso.rs b/src/sso.rs index 9f9a8ab5..56333ee7 100644 --- a/src/sso.rs +++ b/src/sso.rs @@ -289,18 +289,45 @@ pub async fn exchange_code( let email_verified = id_claims.email_verified().or(user_info.email_verified()); let configured_claim = CONFIG.sso_name_claim(); + let mut user_name = None; + + if configured_claim == "name" { + user_name = id_claims + .name() + .and_then(|n| n.get(id_claims.locale().as_ref()).or_else(|| n.get(None))) + .or_else(|| { + user_info + .name() + .and_then(|n| n.get(user_info.locale().as_ref()).or_else(|| n.get(None))) + }) + .map(|n| n.to_string()) + .filter(|s| !s.trim().is_empty()); + } else if !configured_claim.is_empty() { + let extract_claim = |claims: &serde_json::Value, claim_key: &str| -> Option { + claims.get(claim_key) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()) + .filter(|s| !s.trim().is_empty()) + }; - let extract_claim = |claims: &serde_json::Value, claim_key: &str| -> Option { - claims.get(claim_key).and_then(|v| v.as_str()).map(|s| s.to_string()) - }; + let id_claims_json = serde_json::to_value(&id_claims).unwrap_or_default(); + let user_info_json = serde_json::to_value(&user_info).unwrap_or_default(); - let id_claims_json = serde_json::to_value(&id_claims).unwrap_or_default(); - let user_info_json = serde_json::to_value(&user_info).unwrap_or_default(); + user_name = extract_claim(&id_claims_json, &configured_claim) + .or_else(|| extract_claim(&user_info_json, &configured_claim)); + } - let user_name = extract_claim(&id_claims_json, &configured_claim) - .or_else(|| extract_claim(&user_info_json, &configured_claim)) - .or_else(|| id_claims.preferred_username().map(|n| n.to_string())) - .or_else(|| user_info.preferred_username().map(|n| n.to_string())); + let user_name = user_name + .or_else(|| { + id_claims.preferred_username() + .map(|n| n.to_string()) + .filter(|s| !s.trim().is_empty()) + }) + .or_else(|| { + user_info.preferred_username() + .map(|n| n.to_string()) + .filter(|s| !s.trim().is_empty()) + }); let refresh_token = token_response.refresh_token().map(openidconnect::RefreshToken::secret); if refresh_token.is_none() && CONFIG.sso_scopes_vec().contains(&"offline_access".to_owned()) {