Private
Public Access
1
0

feat: OIDC SSO provider support (Keycloak, Azure AD, custom)
All checks were successful
CI Pipeline / Rust Format Check (push) Successful in 4s
CI Pipeline / Clippy Lints (push) Successful in 52s
CI Pipeline / Rust Unit Tests (push) Successful in 1m11s
CI Pipeline / Security Audit (push) Successful in 5s
CI Pipeline / Frontend Lint & Type Check (push) Successful in 15s
CI Pipeline / Build .deb & Release (push) Has been skipped

- Refactored azure_sso.rs to sso.rs with generic OIDC provider support
- Added OIDC discovery URL lookup with 1hr TTL caching
- Added PKCE for all providers, client_secret optional for public clients
- Added /api/v1/auth/sso/login and /api/v1/auth/sso/callback routes
- Added /api/v1/auth/azure/* backward-compatible routes
- Added POST /settings/sso/discover and POST /settings/sso/test endpoints
- Frontend: Provider dropdown (Keycloak/Azure AD/Custom OIDC)
- Frontend: Auto-fill discovery URL for Keycloak
- Frontend: Discover Endpoints and Test Connection buttons
- Frontend: Dynamic SSO button based on provider display name
- Made migration 014 idempotent with DO blocks and IF NOT EXISTS
- Fixed debian/install to use /usr/local/bin/ for binaries
- Fixed frontend file path in .deb package
- Reset admin password on dev server
- Fixed database permissions for oidc_config table
This commit is contained in:
2026-05-13 13:32:24 +00:00
parent e3d8569b05
commit 69d2e88bbd
14 changed files with 883 additions and 496 deletions

View File

@ -0,0 +1,59 @@
-- 014_oidc_provider.sql
-- Migrate from Azure AD-specific SSO to generic OIDC provider support
-- Supports Keycloak, Azure AD, and custom OIDC providers
-- Add new auth_provider enum values for Keycloak and generic OIDC
-- Use DO blocks with exception handling for idempotency
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_enum e JOIN pg_type t ON e.enumtypid = t.oid WHERE t.typname = 'auth_provider' AND e.enumlabel = 'keycloak') THEN
ALTER TYPE auth_provider ADD VALUE 'keycloak';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_enum e JOIN pg_type t ON e.enumtypid = t.oid WHERE t.typname = 'auth_provider' AND e.enumlabel = 'oidc') THEN
ALTER TYPE auth_provider ADD VALUE 'oidc';
END IF;
END
$$;
-- Add oidc_sub column for Keycloak/custom OIDC subject IDs
ALTER TABLE users ADD COLUMN IF NOT EXISTS oidc_sub TEXT;
CREATE INDEX IF NOT EXISTS idx_users_oidc_sub ON users (oidc_sub) WHERE oidc_sub IS NOT NULL;
-- Create oidc_config table (replaces azure_sso_config)
CREATE TABLE IF NOT EXISTS oidc_config (
id INTEGER PRIMARY KEY DEFAULT 1 CHECK (id = 1),
enabled BOOLEAN NOT NULL DEFAULT FALSE,
provider_type TEXT NOT NULL DEFAULT 'azure' CHECK (provider_type IN ('keycloak', 'azure', 'custom')),
display_name TEXT NOT NULL DEFAULT 'Azure AD',
discovery_url TEXT NOT NULL DEFAULT '',
client_id TEXT NOT NULL DEFAULT '',
-- Empty string for public clients (Keycloak); non-empty for confidential clients (Azure AD)
client_secret TEXT NOT NULL DEFAULT '',
redirect_uri TEXT NOT NULL DEFAULT '',
scopes TEXT NOT NULL DEFAULT 'openid profile email',
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Migrate data from azure_sso_config if it has a row and oidc_config is empty
INSERT INTO oidc_config (enabled, provider_type, display_name, discovery_url, client_id, client_secret, redirect_uri, scopes)
SELECT
az.enabled,
'azure',
'Azure AD',
CASE
WHEN az.tenant_id IS NOT NULL AND az.tenant_id != ''
THEN 'https://login.microsoftonline.com/' || az.tenant_id || '/v2.0/.well-known/openid-configuration'
ELSE ''
END,
az.client_id,
az.client_secret,
az.redirect_uri,
az.scopes
FROM azure_sso_config az
WHERE az.id = 1
ON CONFLICT (id) DO NOTHING;
-- Ensure a default row exists if no data was migrated
INSERT INTO oidc_config (enabled, provider_type, display_name)
SELECT FALSE, 'azure', 'Azure AD'
WHERE NOT EXISTS (SELECT 1 FROM oidc_config WHERE id = 1);