import { zodResolver } from "@hookform/resolvers/zod"; import { ShieldCheckIcon, WarningIcon } from "@phosphor-icons/react"; import axios from "axios"; import { useState } from "react"; import { useForm } from "react-hook-form"; import { useLocation, useNavigate } from "react-router-dom"; import { z } from "zod"; import { api, publicApi } from "../api/apiClient"; import Logo from "../components/Logo"; import FormField from "../components/ui/FormField"; import { endpoints } from "../config/endpoints"; import { ROUTES } from "../config/routes"; import { useAuth } from "../hooks/useAuth"; import { CryptoUtils } from "../utils/crypto"; const loginSchema = z.object({ email: z.email("Please enter a valid email"), password: z.string().min(1, "Password is required"), }); type LoginInputs = z.infer; function WelcomeModal({ setShowWelcome }) { return (

Welcome to !

To ensure complete privacy, all your letters are{" "} sealed with your password , which only you have access to.
The server never sees it, and it's a solemn promise!

If you ever happen to forget your password, your letters are lost to time, forever.

); } export default function Login() { const navigate = useNavigate(); const location = useLocation(); const [isLoading, setIsLoading] = useState(false); const [apiError, setApiError] = useState(null); const { setAuthStore } = useAuth(); const [showWelcome, setShowWelcome] = useState(!!location.state?.firstTime); const nextRoute = location.state?.redirectUrl || ROUTES.DRAWER; const { register, handleSubmit, formState: { errors }, } = useForm({ resolver: zodResolver(loginSchema), }); const onSubmit = async (data: LoginInputs) => { setIsLoading(true); setApiError(null); try { // client side key derivation for 0 knowledge const { masterKey, authHash } = await CryptoUtils.deriveKeyBundle( data.password, data.email, ); // send just the authHash as the password to the server const { data: authData } = await publicApi.post(endpoints.LOGIN, { email: data.email, password: authHash, }); const { data: userData } = await api.get(endpoints.ME, { headers: { Authorization: `Bearer ${authData.access}` }, }); // store the auth related data await setAuthStore(authData.access, userData, masterKey); navigate(nextRoute, { replace: true }); } catch (err) { let message = "Sorry, we're experiencing technical issues.\nPlease try again later."; if (axios.isAxiosError(err) && err.response?.status !== 500) { message = err.response?.data?.detail || err.response?.data?.message; } setApiError(message); } finally { setIsLoading(false); } }; return (
{showWelcome && }

Sign in to

{apiError && (
{apiError}
)}
Don't have an account?{" "}
); }