Add support for displaying and managing legacy SSO providers (#628)

* feat: add support for displaying and managing legacy SSO providers

- Introduced UI section for environment/YAML-configured SSO providers.
- Added warnings and guidance on migrating legacy providers to database-backed configuration.
- Enhanced localization with new keys for legacy provider management.
- Updated form and toggle components for improved usability.

* Expand SSO documentation: add SAML 2.0 support, JIT provisioning settings, super-admin setup steps, audit logging, and user administration details.

* Update JIT provisioning docs: clarify role mapping behavior and add examples; note new `logout_idp` audit log event.

---------

Co-authored-by: Josh Waldrep <joshua.waldrep5+github@gmail.com>
This commit is contained in:
LPW
2026-01-13 03:37:19 -05:00
committed by GitHub
parent 6e240a2332
commit 320e087a22
5 changed files with 137 additions and 5 deletions

View File

@@ -7,6 +7,12 @@ module Admin
def index
authorize SsoProvider
@sso_providers = policy_scope(SsoProvider).order(:name)
# Load runtime providers (from YAML/env) that might not be in the database
# This helps show users that legacy providers are active but not manageable via UI
@runtime_providers = Rails.configuration.x.auth.sso_providers || []
db_provider_names = @sso_providers.pluck(:name)
@legacy_providers = @runtime_providers.reject { |p| db_provider_names.include?(p[:name].to_s) }
end
def show

View File

@@ -55,9 +55,13 @@
</div>
</div>
<%= form.check_box :enabled,
label: "Enable this provider",
checked: sso_provider.enabled? %>
<div class="flex items-center justify-between">
<div class="space-y-1">
<p class="text-sm font-medium text-primary"><%= t("admin.sso_providers.form.enabled_label") %></p>
<p class="text-xs text-secondary"><%= t("admin.sso_providers.form.enabled_help") %></p>
</div>
<%= form.toggle :enabled %>
</div>
</div>
<div class="border-t border-primary pt-4 space-y-4">

View File

@@ -62,6 +62,44 @@
</div>
<% end %>
<% if @legacy_providers.any? %>
<%= settings_section title: t("admin.sso_providers.index.legacy_providers_title"), collapsible: true, open: true do %>
<div class="bg-amber-50 border border-amber-200 rounded-lg p-3 mb-4">
<div class="flex gap-2">
<%= icon "alert-triangle", class: "w-5 h-5 text-amber-600 shrink-0" %>
<p class="text-sm text-amber-800">
<%= t("admin.sso_providers.index.legacy_providers_notice") %>
</p>
</div>
</div>
<div class="divide-y divide-primary">
<% @legacy_providers.each do |provider| %>
<div class="flex items-center justify-between py-3 first:pt-0 last:pb-0">
<div class="flex items-center gap-3">
<% provider_icon = provider[:icon].presence || "key" %>
<%= icon provider_icon, class: "w-5 h-5 text-secondary" %>
<div>
<p class="font-medium text-primary"><%= provider[:label].presence || provider[:name] %></p>
<p class="text-sm text-secondary">
<%= provider[:strategy].to_s.titleize %> · <%= provider[:name] %>
<% if provider[:issuer].present? %>
· <span class="text-xs"><%= provider[:issuer] %></span>
<% end %>
</p>
</div>
</div>
<div class="flex items-center gap-2">
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-amber-100 text-amber-800">
<%= t("admin.sso_providers.index.env_configured") %>
</span>
</div>
</div>
<% end %>
</div>
<% end %>
<% end %>
<%= settings_section title: "Configuration Mode", collapsible: true, open: false do %>
<div class="space-y-3">
<div class="flex items-center justify-between">

View File

@@ -18,6 +18,9 @@ en:
actions: "Actions"
enabled: "Enabled"
disabled: "Disabled"
legacy_providers_title: "Environment-Configured Providers"
legacy_providers_notice: "These providers are configured via environment variables or YAML and cannot be managed through this interface. To manage them here, migrate them to database-backed providers by enabling AUTH_PROVIDERS_SOURCE=db and recreating them in the UI."
env_configured: "Env/YAML"
new:
title: "Add SSO Provider"
description: "Configure a new single sign-on authentication provider"
@@ -51,6 +54,7 @@ en:
icon_placeholder: "e.g., key, google, github"
icon_help: "Lucide icon name (optional)"
enabled_label: "Enable this provider"
enabled_help: "Users can sign in with this provider when enabled"
issuer_label: "Issuer"
issuer_placeholder: "https://accounts.google.com"
issuer_help: "OIDC issuer URL (will validate .well-known/openid-configuration endpoint)"

View File

@@ -1,6 +1,6 @@
# Configuring OpenID Connect and SSO providers
# Configuring OpenID Connect, SAML, and SSO Providers
This guide shows how to enable OpenID Connect (OIDC) and other single sign-on (SSO) providers for Sure using Google, GitHub, or another OIDCcompatible identity provider (e.g. Keycloak, Authentik).
This guide shows how to enable OpenID Connect (OIDC), SAML 2.0, and other single sign-on (SSO) providers for Sure using Google, GitHub, or another identity provider (e.g. Keycloak, Authentik, Okta, Azure AD).
It also documents the new `config/auth.yml` and environment variables that control:
@@ -174,6 +174,26 @@ To enable Google:
- `http://localhost:3000/auth/<provider_name>/callback`
### 3.5 Bootstrapping the first superadmin
The first `super_admin` must be set via Rails console. Access the console in your container/pod or directly on the server:
```bash
bin/rails console
```
Then promote a user:
```ruby
# Set super_admin role
User.find_by(email: "admin@example.com").update!(role: :super_admin)
# Verify
User.find_by(email: "admin@example.com").role # => "super_admin"
```
Once set, superadmins can promote other users via the web UI at `/admin/users`.
---
## 4. Example configurations
@@ -419,6 +439,20 @@ To switch back to YAML-based configuration:
2. Restart the application
3. Providers will be loaded from `config/auth.yml`
### 6.6 JIT provisioning settings
Each provider has a **Default Role** field (defaults to `member`) that sets the role for JIT-created users.
**Role mapping from IdP groups:**
Expand **"Role Mapping"** in the admin UI to map IdP group names to Sure roles. Enter comma-separated group names for each role:
- **Super Admin Groups**: `Platform-Admins, IdP-Superusers`
- **Admin Groups**: `Team-Leads, Managers`
- **Member Groups**: `Everyone` or leave blank
Mapping is case-sensitive and matches exact group claim values from the IdP. When a user belongs to multiple mapped groups, the highest role wins (`super_admin` > `admin` > `member`). If no groups match, the Default Role is used.
---
## 7. Troubleshooting
@@ -484,4 +518,50 @@ Each provider requires a callback URL configured in your identity provider:
---
## 9. SAML 2.0 Support
Sure supports SAML 2.0 via database-backed providers. Select **"SAML 2.0"** as the strategy when adding a provider at `/admin/sso_providers`.
Configure with either:
- **IdP Metadata URL** (recommended) - auto-fetches configuration
- **Manual config** - IdP SSO URL + certificate
In your IdP, set:
- **ACS URL**: `https://yourdomain.com/auth/<provider_name>/callback`
- **Entity ID**: `https://yourdomain.com` (your `APP_URL`)
- **Name ID**: Email Address
---
## 10. User Administration
Superadmins can manage user roles at `/admin/users`.
Roles: `member` (standard), `admin` (family admin), `super_admin` (platform admin).
Note: Superadmins cannot change their own role.
---
## 11. Audit Logging
SSO events are logged to `sso_audit_logs`: `login`, `login_failed`, `logout`, `logout_idp` (federated logout), `link`, `unlink`, `jit_account_created`.
Query via console:
```ruby
SsoAuditLog.by_event("login").recent.limit(50)
SsoAuditLog.by_event("login_failed").where("created_at > ?", 24.hours.ago)
```
---
## 12. User SSO Identity Management
Users manage linked SSO identities at **Settings > Security**.
SSO-only users (no password) cannot unlink their last identity.
---
For additional help, see the main [hosting documentation](../README.md) or open an issue on GitHub.