feat(oidc): Show the provider's name and icon on the login page

This commit is contained in:
Erwan Leboucher
2026-09-10 14:08:03 +00:00
committed by Ellis Git
parent 0060a4aac4
commit fe38efbb63
7 changed files with 75 additions and 5 deletions
@@ -0,0 +1 @@
Added the `provider_name` and `provider_icon` OIDC options, which show the identity provider's name and icon on the web login page. Contributed by @eleboucher.
+14
View File
@@ -2271,6 +2271,20 @@
#
#additional_scopes = []
# The name of the identity provider. If this is set, the login page's
# button reads "Continue with <name>" instead of just "Continue".
#
# example: "Kanidm"
#
#provider_name =
# The URL of an icon for the identity provider, shown on the login page
# next to its name.
#
# example: "https://idm.example.com/pkg/img/logo-square.svg"
#
#provider_icon =
# Whether the user should be prompted to choose a localpart
# when signing in for the first time. If this is `false`, Continuwuity
# will attempt to use the value of the `preferred_username_claim`
+12
View File
@@ -2749,6 +2749,18 @@ pub struct OidcConfig {
#[serde(default)]
pub additional_scopes: Vec<Scope>,
/// The name of the identity provider. If this is set, the login page's
/// button reads "Continue with <name>" instead of just "Continue".
///
/// example: "Kanidm"
pub provider_name: Option<String>,
/// The URL of an icon for the identity provider, shown on the login page
/// next to its name.
///
/// example: "https://idm.example.com/pkg/img/logo-square.svg"
pub provider_icon: Option<Url>,
/// Whether the user should be prompted to choose a localpart
/// when signing in for the first time. If this is `false`, Continuwuity
/// will attempt to use the value of the `preferred_username_claim`
+12
View File
@@ -219,6 +219,18 @@ impl Service {
pub fn enabled(&self) -> bool { self.client.is_some() }
pub fn provider_name(&self) -> Option<String> {
let OidcClient { config, .. } = self.client.as_ref().expect("oidc should be configured");
config.provider_name.clone()
}
pub fn provider_icon(&self) -> Option<Url> {
let OidcClient { config, .. } = self.client.as_ref().expect("oidc should be configured");
config.provider_icon.clone()
}
pub fn restricted_profile_fields(&self) -> Vec<ProfileFieldName> {
if let Some(config) = self.client.as_ref().map(|client| &client.config)
&& matches!(config.profile_key_import_mode, OidcProfileKeyImportMode::OnLogin)
+7 -1
View File
@@ -61,6 +61,8 @@ enum LoginType {
},
Oidc {
redirect_url: Url,
provider_name: Option<String>,
provider_icon: Option<Url>,
},
}
@@ -98,7 +100,11 @@ async fn route_login(
.await
.expect("should be able to serialize OIDC session");
LoginType::Oidc { redirect_url }
LoginType::Oidc {
redirect_url,
provider_name: services.oidc.provider_name(),
provider_icon: services.oidc.provider_icon(),
}
} else {
LoginType::Interactive {
registration_available: services
+12
View File
@@ -7,6 +7,18 @@
}
}
a.button.provider-button {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5em;
}
.provider-icon {
flex: none;
height: 2em;
}
.text-rule {
display: flex;
align-items: center;
+17 -4
View File
@@ -8,6 +8,19 @@
Log in
{%- endblock -%}
{%- macro oidc_button(redirect_url, provider_name, provider_icon) -%}
<a class="button provider-button" href="{{ redirect_url }}">
{% if let Some(icon) = provider_icon %}
<img class="provider-icon" src="{{ icon }}" alt="" aria-hidden="true">
{% endif %}
{% if let Some(name) = provider_name %}
Continue with {{ name }}
{% else %}
Continue
{% endif %}
</a>
{%- endmacro -%}
{%- block content -%}
{% match login_type %}
{% when LoginType::Interactive { .. } %}
@@ -54,8 +67,8 @@ Log in
{% endif %}
<a href="{{ crate::ROUTE_PREFIX }}/account/password/reset/">Forgot your password?</a>
</div>
{% when LoginType::Oidc { redirect_url } %}
<a class="button" href="{{ redirect_url }}">Continue</a>
{% when LoginType::Oidc { redirect_url, provider_name, provider_icon } %}
{{ oidc_button(redirect_url, provider_name, provider_icon) }}
{% endmatch %}
{% when LoginBody::Authenticated { user_card } %}
<h1>Confirm your identity</h1>
@@ -76,8 +89,8 @@ Log in
<div class="centered-links">
<a href="{{ crate::ROUTE_PREFIX }}/account/password/reset/">Forgot your password?</a>
</div>
{% when LoginType::Oidc { redirect_url } %}
<a class="button" href="{{ redirect_url }}">Continue</a>
{% when LoginType::Oidc { redirect_url, provider_name, provider_icon } %}
{{ oidc_button(redirect_url, provider_name, provider_icon) }}
{% endmatch %}
{% endmatch %}
</div>