import { useState, useRef } from 'react'; import { useAuthProviders } from '../../hooks/useSecurity'; import { Button } from '../../components/ui/Button'; import { Plus, Edit, Trash2, Globe } from 'lucide-react'; import toast from 'react-hot-toast'; import type { AuthProvider, CreateAuthProviderRequest, UpdateAuthProviderRequest } from '../../api/security'; interface ProviderFormData { name: string; type: 'google' | 'github' | 'oidc'; client_id: string; client_secret: string; issuer_url: string; auth_url: string; token_url: string; user_info_url: string; scopes: string; display_name: string; enabled: boolean; } interface HelpTooltipProps { content: React.ReactNode; position?: 'left' | 'right'; } const HelpTooltip = ({ content, position = 'left' }: HelpTooltipProps) => { const [isOpen, setIsOpen] = useState(false); const timeoutRef = useRef | null>(null); const handleMouseEnter = () => { if (timeoutRef.current) clearTimeout(timeoutRef.current); setIsOpen(true); }; const handleMouseLeave = () => { timeoutRef.current = setTimeout(() => { setIsOpen(false); }, 300); }; return (
{isOpen && (
{content}
)}
); }; export default function Providers() { const { providers, createProvider, updateProvider, deleteProvider, isLoading } = useAuthProviders(); const [isModalOpen, setIsModalOpen] = useState(false); const [editingProvider, setEditingProvider] = useState(null); const [formData, setFormData] = useState({ name: '', type: 'oidc', client_id: '', client_secret: '', issuer_url: '', auth_url: '', token_url: '', user_info_url: '', scopes: 'openid,profile,email', display_name: '', enabled: true, }); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { if (editingProvider) { const updateData: UpdateAuthProviderRequest = { name: formData.name, client_id: formData.client_id, issuer_url: formData.issuer_url, auth_url: formData.auth_url, token_url: formData.token_url, user_info_url: formData.user_info_url, scopes: formData.scopes, display_name: formData.display_name, enabled: formData.enabled, }; if (formData.client_secret) { updateData.client_secret = formData.client_secret; } await updateProvider({ uuid: editingProvider.uuid, data: updateData }); toast.success('Provider updated successfully'); } else { const createData: CreateAuthProviderRequest = { name: formData.name, type: formData.type, client_id: formData.client_id, client_secret: formData.client_secret, issuer_url: formData.issuer_url, auth_url: formData.auth_url, token_url: formData.token_url, user_info_url: formData.user_info_url, scopes: formData.scopes, display_name: formData.display_name, }; await createProvider(createData); toast.success('Provider created successfully'); } setIsModalOpen(false); resetForm(); } catch (error: unknown) { const err = error as { response?: { data?: { error?: string } } }; toast.error(err.response?.data?.error || 'Failed to save provider'); } }; const handleDelete = async (uuid: string) => { if (confirm('Are you sure you want to delete this provider?')) { try { await deleteProvider(uuid); toast.success('Provider deleted successfully'); } catch (error: unknown) { const err = error as { response?: { data?: { error?: string } } }; toast.error(err.response?.data?.error || 'Failed to delete provider'); } } }; const resetForm = () => { setFormData({ name: '', type: 'oidc', client_id: '', client_secret: '', issuer_url: '', auth_url: '', token_url: '', user_info_url: '', scopes: 'openid,profile,email', display_name: '', enabled: true, }); setEditingProvider(null); }; const openEditModal = (provider: AuthProvider) => { setEditingProvider(provider); setFormData({ name: provider.name, type: provider.type, client_id: provider.client_id, client_secret: '', // Don't populate secret issuer_url: provider.issuer_url || '', auth_url: provider.auth_url || '', token_url: provider.token_url || '', user_info_url: provider.user_info_url || '', scopes: provider.scopes || 'openid,profile,email', display_name: provider.display_name || '', enabled: provider.enabled, }); setIsModalOpen(true); }; if (isLoading) return
Loading...
; return (

Identity Providers

{providers.map((provider) => (

{provider.name}

{provider.type}

Client ID: {provider.client_id}
Status: {provider.enabled ? ( Active ) : ( Disabled )}
))} {providers.length === 0 && (
No identity providers configured. Add one to enable external authentication.
)}
{/* Provider Modal */} {isModalOpen && (

{editingProvider ? 'Edit Provider' : 'Add Provider'}

setFormData({ ...formData, name: e.target.value })} placeholder="Google" className="w-full bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 text-white focus:ring-2 focus:ring-blue-500" />
The public identifier for your OAuth application.
} />
setFormData({ ...formData, client_id: e.target.value })} placeholder="e.g., 123456789.apps.googleusercontent.com" className="w-full bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 text-white focus:ring-2 focus:ring-blue-500" />
The private key for your OAuth application. Keep this secret and secure!
} />
setFormData({ ...formData, client_secret: e.target.value })} placeholder="Enter your client secret" className="w-full bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 text-white focus:ring-2 focus:ring-blue-500" />
{formData.type === 'oidc' && (
setFormData({ ...formData, issuer_url: e.target.value })} placeholder="https://accounts.google.com" className="w-full bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 text-white focus:ring-2 focus:ring-blue-500" />
)}
setFormData({ ...formData, scopes: e.target.value })} className="w-full bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 text-white focus:ring-2 focus:ring-blue-500" />
setFormData({ ...formData, display_name: e.target.value })} placeholder="Sign in with Google" className="w-full bg-gray-900 border border-gray-700 rounded-lg px-3 py-2 text-white focus:ring-2 focus:ring-blue-500" />
setFormData({ ...formData, enabled: e.target.checked })} className="w-4 h-4 text-blue-600 bg-gray-900 border-gray-700 rounded focus:ring-blue-500" />
)}
); }