import React, { useEffect, useState } from 'react'; import { useNavigate, Link, useSearchParams } from 'react-router-dom'; import { useAuth } from '../context/AuthContext'; import { Logo } from '../components/Logo'; import * as api from '../api'; import { USER_KEY } from '../utils/impersonation'; import { getPasswordMinLength, getPasswordRequirementsLabel, validatePasswordForCurrentEnv, } from '../utils/passwordPolicy'; export const Login: React.FC = () => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [newPassword, setNewPassword] = useState(''); const [confirmNewPassword, setConfirmNewPassword] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); const { login, logout, authEnabled, oidcEnabled, oidcEnforced, oidcProvider, bootstrapRequired, authOnboardingRequired, isAuthenticated, loading: authLoading, user, } = useAuth(); const navigate = useNavigate(); const [searchParams] = useSearchParams(); const queryMustReset = searchParams.get('mustReset') === '1'; const oidcErrorCode = searchParams.get('oidcError'); const oidcErrorMessage = searchParams.get('oidcErrorMessage'); const oidcReturnTo = searchParams.get('returnTo') || '/'; const mustReset = Boolean(user?.mustResetPassword) || queryMustReset; useEffect(() => { if (!oidcErrorCode) return; setError(oidcErrorMessage || 'OIDC sign-in failed'); }, [oidcErrorCode, oidcErrorMessage]); useEffect(() => { if (authLoading || authEnabled === null) return; if (authOnboardingRequired) { navigate('/auth-setup', { replace: true }); return; } if (!authEnabled) { navigate('/', { replace: true }); return; } if (bootstrapRequired) { navigate('/register', { replace: true }); return; } if (oidcEnforced && !mustReset) { if (!oidcErrorCode) { api.startOidcSignIn(oidcReturnTo); } return; } if (isAuthenticated) { if (mustReset) return; navigate('/', { replace: true }); } }, [ authEnabled, authLoading, authOnboardingRequired, bootstrapRequired, isAuthenticated, mustReset, navigate, oidcEnforced, oidcErrorCode, oidcReturnTo, ]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(''); setLoading(true); try { await login(email, password); const stored = localStorage.getItem(USER_KEY); const storedUser = stored ? (JSON.parse(stored) as { mustResetPassword?: boolean } | null) : null; if (storedUser?.mustResetPassword) { setPassword(''); return; } navigate('/'); } catch (err: unknown) { const message = err instanceof Error ? err.message : 'Failed to login'; setError(message); } finally { setLoading(false); } }; const handleMustReset = async (e: React.FormEvent) => { e.preventDefault(); setError(''); if (!newPassword || !confirmNewPassword) { setError('Please enter and confirm a new password'); return; } const passwordError = validatePasswordForCurrentEnv(newPassword, 'New password'); if (passwordError) { setError(passwordError); return; } if (newPassword !== confirmNewPassword) { setError('New passwords do not match'); return; } setLoading(true); try { const response = await api.api.post<{ user: { id: string; email: string; name: string; role?: string; mustResetPassword?: boolean }; accessToken: string; refreshToken: string; }>('/auth/must-reset-password', { newPassword }); localStorage.setItem(USER_KEY, JSON.stringify(response.data.user)); window.location.href = '/'; } catch (err: unknown) { let message = 'Failed to reset password'; if (api.isAxiosError(err)) { message = err.response?.data?.message || err.response?.data?.error || message; } setError(message); } finally { setLoading(false); } }; return (

{mustReset ? 'Reset your password' : oidcEnforced ? `Sign in with ${oidcProvider || 'OIDC'}` : 'Sign in to your account'}

{!mustReset && !oidcEnforced ? (

Or{' '} create a new account

) : mustReset ? (

Your admin requires you to set a new password before using ExcaliDash.

) : (

You will be redirected to {oidcProvider || 'your identity provider'}.

)}
{error && (
{error}
)} {oidcEnforced && !mustReset ? (
) : (
{!mustReset ? ( <>
setEmail(e.target.value)} />
setPassword(e.target.value)} />
) : ( <>
setNewPassword(e.target.value)} />
setConfirmNewPassword(e.target.value)} />
)}
)} {!mustReset && !oidcEnforced && (
Forgot your password?
)} {(!oidcEnforced || mustReset) && (
)} {!mustReset && oidcEnabled && !oidcEnforced && (
)} {mustReset && (
)}
); };