import { useEffect, useState, type FormEvent } from 'react' import { useAuth } from '../../contexts/AuthContext' import api from '../../services/api' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Alert, AlertDescription } from '@/components/ui/alert' interface OidcConfig { enabled: boolean label: string login_url: string } interface AuthConfig { oidc: OidcConfig | null } export function LoginPage() { const { login } = useAuth() const [username, setUsername] = useState('') const [password, setPassword] = useState('') const [error, setError] = useState(null) const [loading, setLoading] = useState(false) const [oidc, setOidc] = useState(null) // While the OIDC config loads we may auto-bounce to the IdP. Hide // the form until we know we're staying so the user doesn't see a // flash of password fields right before the redirect kicks in. const [autoRedirecting, setAutoRedirecting] = useState(true) // Ask the backend which login methods to show. If OIDC is enabled // and the user already has an SSO session at the IdP, the natural // flow is for them to land here, get bounced through Authentik, and // come straight back signed in — without ever clicking a button. // Two escape hatches: `?password=1` in the URL for explicit password // login, and a `skipAutoSso` sessionStorage flag set by logout and // by the OIDC callback's error branch so users don't get trapped in // a redirect loop. useEffect(() => { let cancelled = false ;(async () => { try { const res = await api.get('/auth/config') if (cancelled) return const cfg = res.data.oidc setOidc(cfg) if (!cfg?.enabled) { setAutoRedirecting(false) return } const params = new URLSearchParams(window.location.search) if ( params.has('password') || sessionStorage.getItem('skipAutoSso') === '1' ) { sessionStorage.removeItem('skipAutoSso') setAutoRedirecting(false) return } window.location.href = cfg.login_url } catch { if (!cancelled) setAutoRedirecting(false) } })() return () => { cancelled = true } }, []) const handleSubmit = async (e: FormEvent) => { e.preventDefault() setError(null) setLoading(true) try { await login(username, password) } catch (err: any) { setError( err.response?.data?.detail ?? 'Unable to sign in. Check your credentials.', ) } finally { setLoading(false) } } if (autoRedirecting) { return (
Signing in with {oidc?.label ?? 'identity provider'}…
setAutoRedirecting(false)} > Use password instead
) } return (

Sign in to Mulita

{error && ( {error} )} {oidc?.enabled && ( <> {/* Full-page navigation (not a fetch) — Authlib sets a * signed session cookie in the /oidc/login response, so * the browser needs to follow the redirect chain itself. */}
or continue with password
)}
setUsername(e.target.value)} required autoFocus />
setPassword(e.target.value)} required />
) }