import { useState, FormEvent } from 'react'; import { Link, useNavigate, useLocation } from 'react-router-dom'; import { Shield, Lock } from 'lucide-react'; import api from '../lib/api'; import { useAuthStore } from '../lib/store'; import { deriveKeyFromPassword, decryptMasterKey, decryptPrivateKeyToCryptoKey } from '../crypto'; import { socket } from '../lib/ws'; export default function LoginPage() { const navigate = useNavigate(); const location = useLocation(); const { setAuth, setMasterKey, setKeyPair, setUsername, username, email: storedEmail } = useAuthStore(); // Reauth: tokens still valid but crypto keys lost on refresh const isReauth = location.state?.reauth === true && !!storedEmail; const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [totpCode, setTotpCode] = useState(''); const [require2fa, setRequire2fa] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); async function submit(e: FormEvent) { e.preventDefault(); setError(''); setLoading(true); try { const res = await api.post('/auth/login', { email: isReauth ? storedEmail : email, password, ...(totpCode ? { totpCode } : {}), }); if (res.data.require2fa) { setRequire2fa(true); setLoading(false); return; } const { accessToken, refreshToken, userId, username: uname, kdfSalt, encryptedMasterKey, isAdmin } = res.data; const derived = await deriveKeyFromPassword(password, kdfSalt); const masterKey = await decryptMasterKey(encryptedMasterKey, derived); setAuth({ accessToken, refreshToken, userId, isAdmin, email: isReauth ? storedEmail! : email }); setMasterKey(masterKey); setUsername(uname); const keyRes = await api.get('/auth/my-key'); const privateKey = await decryptPrivateKeyToCryptoKey(keyRes.data.encrypted_private_key, masterKey); setKeyPair(keyRes.data.public_key, privateKey); socket.connect(accessToken); navigate('/messages'); } catch (err: any) { setError(err.response?.data?.error || 'Login failed'); } finally { setLoading(false); } } return (
{isReauth ? ( ) : ( )}

CloudLink

{isReauth ? `Welcome back, ${username}` : 'End-to-end encrypted'}

{isReauth ? 'Enter your password to unlock' : 'Sign in'}

{error &&

{error}

} {isReauth ? ( ) : !require2fa ? ( <> ) : (
setTotpCode(e.target.value)} autoFocus />
)} {isReauth && ( )} {!isReauth && (

No account?{' '} Register

)}
); } function Field({ label, type, value, onChange, autoFocus }: { label: string; type: string; value: string; onChange: (v: string) => void; autoFocus?: boolean; }) { return (
onChange(e.target.value)} autoFocus={autoFocus} required />
); }