feat(auth): mobile-only register/login with OTP verification

- Register and login by mobile number; email is now an optional
  contact field only (never used to authenticate)
- After registration, the phone is verified via a 6-digit SMS code
- Login supports both password and one-time-code (OTP) methods
- Phone OTP delivered via Kavenegar (verify/lookup); API key in env
- Account page: edit name/optional email, change password, and
  change mobile number with OTP re-verification
- Codes are hashed, expire in 5m, capped at 5 attempts, rate-limited
- Seed gives the admin a verified phone so mobile login still works

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
keyhan
2026-06-16 16:40:08 +03:30
parent ce6813db99
commit 37c103fa20
31 changed files with 1756 additions and 143 deletions
+11 -9
View File
@@ -21,9 +21,9 @@ export function AuthShell({
title: string;
subtitle: string;
children: ReactNode;
altPrompt: string;
altHref: string;
altLabel: string;
altPrompt?: string;
altHref?: string;
altLabel?: string;
}) {
const t = useT();
return (
@@ -69,12 +69,14 @@ export function AuthShell({
<div className="abrban-panel rounded-[1.75rem] p-6 sm:p-8">{children}</div>
<p className="abrban-ink mt-6 text-center text-sm text-white/90">
{altPrompt}{' '}
<Link href={altHref} className="font-bold text-primary-200 underline-offset-4 hover:text-white hover:underline">
{altLabel}
</Link>
</p>
{altPrompt && altHref && altLabel && (
<p className="abrban-ink mt-6 text-center text-sm text-white/90">
{altPrompt}{' '}
<Link href={altHref} className="font-bold text-primary-200 underline-offset-4 hover:text-white hover:underline">
{altLabel}
</Link>
</p>
)}
</div>
</main>
</div>