"use client"; import { authClient } from "@/lib/auth-client"; import { zodResolver } from "@hookform/resolvers/zod"; import { Eye, EyeOff, KeyRound, Loader2, TriangleAlert } from "lucide-react"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "../ui/accordion"; import { Button } from "../ui/button"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "../ui/form"; import { Input } from "../ui/input"; import IdentityBackup from "./IdentityBackup"; const createIdentityFormSchema = z.object({ password: z .string() .min(8, "Password must be at least 8 characters") .max(64, "Password cannot exceed 64 characters") .refine((val) => /[A-Z]/.test(val), { message: "Must contain at least one uppercase letter", }) .refine((val) => /[a-z]/.test(val), { message: "Must contain at least one lowercase letter", }) .refine((val) => /[0-9]/.test(val), { message: "Must contain at least one number", }) .refine((val) => /[^a-zA-Z0-9]/.test(val), { message: "Must contain at least one special character", }) }); const requirements = [ { label: "8–64 characters", test: (v: string) => v.length >= 8 && v.length <= 64 }, { label: "Uppercase letter", test: (v: string) => /[A-Z]/.test(v) }, { label: "Lowercase letter", test: (v: string) => /[a-z]/.test(v) }, { label: "Number", test: (v: string) => /[0-9]/.test(v) }, { label: "Special character", test: (v: string) => /[^a-zA-Z0-9]/.test(v) }, ]; export default function CreateIdentity() { const [isOpen, setIsOpen] = useState(true); const [showPassword, setShowPassword] = useState(false); const [mnemonic, setMnemonic] = useState(null); const { data: session } = authClient.useSession(); const router = useRouter(); const form = useForm>({ resolver: zodResolver(createIdentityFormSchema), defaultValues: { password: "", }, }); const password = form.watch("password"); const passwordRequirementsMet = requirements.every((req) => req.test(password)); async function onSubmit(values: z.infer) { const userId = session?.user.id; if (!userId) return; try { const result = await authClient.createOvenIdentity(userId, values.password); setMnemonic(result.mnemonic); } catch (err) { console.error("[CreateIdentity]", err); toast.error("Failed to create identity. Please try again."); } } function handleBackupConfirmed() { setIsOpen(false); router.refresh(); } return ( e.preventDefault()} className="sm:max-w-md border-border bg-card p-0 overflow-hidden [&>button]:hidden"> {mnemonic ? ( ) : ( <>
Identity Setup
Create your Sipher identity

This password encrypts your local identity key. It never leaves your device. DO NOT FORGET THIS PASSWORD.

You may use the same password for your Sipher account and your identity, although it is not recommended.

( Master Password
)} /> {password.length > 0 && (
Requirements
    {requirements.map((req) => { const met = req.test(password); return (
  • {req.label}
  • ); })}
)} What if I lose my password?
  • Your identity key is encrypted locally with this password. There is no recovery mechanism.
  • Losing it means permanent loss of access to your encrypted messages and posts.
  • Store it somewhere safe — a password manager or written offline.
  • Losing your identity means that all your messages are permanently lost and your old posts won't hold a valid signature.
)}
) }