import { zodResolver } from "@hookform/resolvers/zod"; import { HandPalmIcon, 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 Saajan from "../components/ui/Saajan"; 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, }: { setShowWelcome: (show: boolean) => void; }) { return (

Welcome to    !

Before we begin, let me make a small promise.


Everything you write here is sealed with your password,{" "} cryptographically , before it leaves your hands.
A fancy way of saying, I couldn't if I tried.

If you ever happen to forget your password, your letters are lost to time, forever.
I highly, highly recommend storing this password in your{" "} password manager {" "} or somewhere safe to remember it.

); } 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 [saajanMessage, setSaajanMessage] = useState( "I was wondering when you'd return.", ); 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 && } {showWelcome && }

Enter Archive

{apiError && (
{apiError}
)} setSaajanMessage("I remember you.")} /> setSaajanMessage("The one thing I cannot know for you.") } />
Don't have an account?{" "}
); }