import { useEffect, useState } from 'react'; import { useAuth } from '@/context/AuthContext'; import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Checkbox } from '@/components/ui/checkbox'; import { ArrowRight, KeyRound, Loader2 } from 'lucide-react'; import { AuthCanvas } from '@/components/auth/AuthCanvas'; import { AuthStepHeader } from '@/components/auth/AuthStepHeader'; import { ErrorRail } from '@/components/auth/ErrorRail'; interface SSOProvider { provider: string; displayName: string; type: 'ldap' | 'oidc'; } const INPUT_CLASS = 'h-11 bg-background/60 border-card-border font-sans text-base shadow-[inset_0_2px_4px_0_oklch(0_0_0/0.25)] placeholder:text-stat-subtitle/60 focus-visible:border-brand/60 focus-visible:ring-2 focus-visible:ring-brand/40 focus-visible:ring-offset-0'; function getProviderIcon(provider: string) { switch (provider) { case 'oidc_google': return ( ); case 'oidc_github': return ( ); case 'oidc_okta': return ( ); case 'oidc_custom': default: return ; } } export function Login({ className, ...props }: React.ComponentPropsWithoutRef<'div'>) { const { login, ssoLdapLogin } = useAuth(); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(() => { const params = new URLSearchParams(window.location.search); const ssoError = params.get('sso_error'); if (ssoError) { window.history.replaceState({}, '', window.location.pathname); return ssoError; } return ''; }); const [isLoading, setIsLoading] = useState(false); const [loginMode, setLoginMode] = useState<'local' | 'ldap'>('local'); const [ssoProviders, setSsoProviders] = useState([]); const [capsLock, setCapsLock] = useState(false); const [rememberMe, setRememberMe] = useState(false); useEffect(() => { fetch('/api/auth/sso/providers', { credentials: 'include' }) .then((r) => (r.ok ? r.json() : [])) .then((providers: SSOProvider[]) => setSsoProviders(providers)) .catch((e) => { console.warn('[Login] SSO provider discovery failed:', e); }); }, []); const hasLdap = ssoProviders.some((p) => p.type === 'ldap'); const oidcProviders = ssoProviders.filter((p) => p.type === 'oidc'); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(''); setIsLoading(true); const result = loginMode === 'ldap' && ssoLdapLogin ? await ssoLdapLogin(username, password, rememberMe) : await login(username, password, rememberMe); if (!result.success) setError(result.error || 'Login failed'); setIsLoading(false); }; const handlePasswordKey = (e: React.KeyboardEvent) => { if (typeof e.getModifierState === 'function') { setCapsLock(e.getModifierState('CapsLock')); } }; return (
Console ยท Local Secure by default
} >
{hasLdap && (
setLoginMode('local')} /> setLoginMode('ldap')} />
)}
setUsername(e.target.value)} className={INPUT_CLASS} />
{capsLock && ( Caps Lock On )}
setPassword(e.target.value)} onKeyDown={handlePasswordKey} onKeyUp={handlePasswordKey} className={INPUT_CLASS} />
setRememberMe(c === true)} />
{error && {error}}
{oidcProviders.length > 0 && (
Or continue with
{oidcProviders.map((p) => ( ))}
)}
); } function ModePill({ active, label, onClick }: { active: boolean; label: string; onClick: () => void }) { return ( ); }