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

@ -212,6 +212,8 @@ export const reportsApi = {
}),
}
// ── Settings API (M10) ────────────────────────────────────────────────────
/** @deprecated Use OidcConfigResponse instead */
export interface AzureSsoConfig {
enabled: boolean
tenant_id: string
@ -220,6 +222,27 @@ export interface AzureSsoConfig {
scopes: string
}
export interface OidcConfigResponse {
enabled: boolean
provider_type: 'keycloak' | 'azure' | 'custom'
display_name: string
discovery_url: string
client_id: string
client_secret: string
redirect_uri: string
scopes: string
}
export interface OidcDiscoveryResult {
success: boolean
issuer: string
authorization_endpoint: string
token_endpoint: string
jwks_uri: string
userinfo_endpoint?: string | null
message?: string
}
export interface SmtpConfig {
enabled: boolean
host: string
@ -241,12 +264,13 @@ export interface NotificationConfig {
}
export interface SettingsResponse {
azure_sso: AzureSsoConfig
oidc: OidcConfigResponse
smtp: SmtpConfig
polling: PollingConfig
ip_whitelist: string[]
web_tls_strategy: string
notification: NotificationConfig
sso_callback_url?: string
}
export interface TestResult {
@ -267,11 +291,14 @@ export interface AuditIntegrityResult {
export const settingsApi = {
get: () => apiClient.get<SettingsResponse>('/settings'),
update: (data: Partial<SettingsResponse> & {
azure_sso?: AzureSsoConfig & { client_secret?: string }
oidc?: OidcConfigResponse & { client_secret?: string }
smtp?: SmtpConfig & { password?: string }
notification?: NotificationConfig
}) => apiClient.put<SettingsResponse>('/settings', data),
testAzureSso: () => apiClient.post<TestResult>('/settings/azure-sso/test'),
discoverOidc: (discoveryUrl: string) => apiClient.post<OidcDiscoveryResult>('/settings/sso/discover', { discovery_url: discoveryUrl }),
testOidc: () => apiClient.post<TestResult>('/settings/sso/test'),
/** @deprecated Use testOidc instead */
testAzureSso: () => apiClient.post<TestResult>('/settings/sso/test'),
testSmtp: () => apiClient.post<TestResult>('/settings/smtp/test'),
getIpWhitelist: () => apiClient.get<{ entries: string[] }>('/settings/ip-whitelist'),
updateIpWhitelist: (entries: string[]) => apiClient.put<{ entries: string[] }>('/settings/ip-whitelist', { entries }),