'use client' import React, { useEffect, useState } from 'react' import { AlertTriangle, Eye, EyeOff, Loader2, Plus, Trash2, X, } from 'lucide-react' import type { RoleMapping, SSOConfig, SSOFormData } from '../_types' import { AVAILABLE_ROLES, EMPTY_FORM } from './constants' import { getDefaultRedirectUri } from './helpers' import { CopyButton } from './shared' export function SSOConfigFormModal({ isOpen, editConfig, onClose, onSave, }: { isOpen: boolean editConfig: SSOConfig | null onClose: () => void onSave: (data: SSOFormData) => Promise }) { const [formData, setFormData] = useState(EMPTY_FORM) const [saving, setSaving] = useState(false) const [error, setError] = useState(null) const [showSecret, setShowSecret] = useState(false) useEffect(() => { if (editConfig) { setFormData({ name: editConfig.name, provider_type: 'oidc', issuer_url: editConfig.oidc_issuer_url || '', client_id: editConfig.oidc_client_id || '', client_secret: '', redirect_uri: editConfig.oidc_redirect_uri || '', scopes: (editConfig.oidc_scopes || []).join(','), auto_provision: editConfig.auto_provision, default_role: editConfig.default_role_id || 'user', role_mappings: Object.entries(editConfig.role_mapping || {}).map(([k, v]) => ({ sso_group: k, internal_role: v })), }) } else { setFormData({ ...EMPTY_FORM, redirect_uri: getDefaultRedirectUri(), }) } setError(null) setShowSecret(false) }, [editConfig, isOpen]) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setError(null) if (!formData.name.trim()) { setError('Name ist erforderlich') return } if (!formData.issuer_url.trim()) { setError('Issuer URL ist erforderlich') return } if (!formData.client_id.trim()) { setError('Client ID ist erforderlich') return } if (!editConfig && !formData.client_secret.trim()) { setError('Client Secret ist erforderlich') return } setSaving(true) try { await onSave(formData) onClose() } catch (err: unknown) { setError(err instanceof Error ? err.message : 'Fehler beim Speichern') } finally { setSaving(false) } } const addRoleMapping = () => { setFormData(prev => ({ ...prev, role_mappings: [...prev.role_mappings, { sso_group: '', internal_role: 'user' }], })) } const updateRoleMapping = (index: number, field: keyof RoleMapping, value: string) => { setFormData(prev => ({ ...prev, role_mappings: prev.role_mappings.map((m, i) => i === index ? { ...m, [field]: value } : m ), })) } const removeRoleMapping = (index: number) => { setFormData(prev => ({ ...prev, role_mappings: prev.role_mappings.filter((_, i) => i !== index), })) } if (!isOpen) return null return (
{/* Header */}

{editConfig ? 'SSO-Konfiguration bearbeiten' : 'Neue SSO-Konfiguration'}

{/* Form */}
{error && (
{error}
)} {/* Provider Type */}
{/* Name */}
setFormData(prev => ({ ...prev, name: e.target.value }))} placeholder="z.B. Azure AD, Okta, Keycloak" className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />
{/* Issuer URL */}
setFormData(prev => ({ ...prev, issuer_url: e.target.value }))} placeholder="https://login.microsoftonline.com/{tenant-id}/v2.0" className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm font-mono focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />

Die Discovery-URL wird automatisch unter /.well-known/openid-configuration abgefragt

{/* Client ID */}
setFormData(prev => ({ ...prev, client_id: e.target.value }))} placeholder="Application (client) ID" className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm font-mono focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />
{/* Client Secret */}
setFormData(prev => ({ ...prev, client_secret: e.target.value }))} placeholder={editConfig ? '********' : 'Client Secret eingeben'} className="w-full px-3 py-2 pr-10 border border-slate-300 rounded-lg text-sm font-mono focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />
{/* Redirect URI */}
setFormData(prev => ({ ...prev, redirect_uri: e.target.value }))} className="flex-1 px-3 py-2 border border-slate-300 rounded-lg text-sm font-mono bg-slate-50 focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />

Diese URI muss im Identity Provider als erlaubte Redirect-URI eingetragen werden

{/* Scopes */}
setFormData(prev => ({ ...prev, scopes: e.target.value }))} placeholder="openid,email,profile" className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />

Komma-getrennte Liste von OIDC Scopes

{/* Auto-Provision Toggle */}

Benutzer automatisch beim ersten Login anlegen

{/* Default Role */}

Rolle fuer neu provisionierte Benutzer ohne Rollen-Mapping

{/* Role Mappings */}
{formData.role_mappings.length === 0 ? (

Keine Rollen-Mappings konfiguriert. Alle Benutzer erhalten die Standard-Rolle.

) : (
{formData.role_mappings.map((mapping, idx) => (
updateRoleMapping(idx, 'sso_group', e.target.value)} placeholder="SSO-Gruppe (z.B. admins)" className="flex-1 px-3 py-2 border border-slate-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-purple-500" />
))}
)}
{/* Footer */}
) }