From 83b50720110077bc90c266d8f685feaa75cd256b Mon Sep 17 00:00:00 2001 From: Ashwin Kumar Sivakumar Date: Thu, 23 Jul 2026 22:25:31 +0530 Subject: [PATCH] Fix forgot-password flow: UI never let users enter the emailed code The backend emails a plain 6-digit reset code (crates/email/templates/ password-reset.html just renders {{reset_code}}, no link) and expects POST /api/auth/reset-password with {code, new_password}. This page was built for a different, unused link-based flow instead: it only read a `token` from the URL query string and posted it as `token`, a field name the backend's ResetPasswordPayload doesn't even have. Users had no way to type the code in at all, so submitting a request just left them stuck back on the request screen. Now: requesting a reset code moves straight to a "set new password" step with a 6-digit code input, and the code is submitted under the correct `code` field name. Co-Authored-By: Claude Sonnet 5 --- src/routes/forgot-password.tsx | 47 ++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/routes/forgot-password.tsx b/src/routes/forgot-password.tsx index f2f1b83..fd372c9 100644 --- a/src/routes/forgot-password.tsx +++ b/src/routes/forgot-password.tsx @@ -27,7 +27,7 @@ export default function ForgotPasswordRoute() { const navigate = useNavigate(); const [search] = useSearchParams(); const [email, setEmail] = createSignal(''); - const [token, setToken] = createSignal(search.token || ''); + const [code, setCode] = createSignal(typeof search.code === 'string' ? search.code : ''); const [newPassword, setNewPassword] = createSignal(''); const [confirmPassword, setConfirmPassword] = createSignal(''); const [showPassword, setShowPassword] = createSignal(false); @@ -35,7 +35,7 @@ export default function ForgotPasswordRoute() { const [error, setError] = createSignal(''); const [success, setSuccess] = createSignal(''); const [submitting, setSubmitting] = createSignal(false); - const [step, setStep] = createSignal<'request' | 'reset'>(token() ? 'reset' : 'request'); + const [step, setStep] = createSignal<'request' | 'reset'>('request'); const passwordsMatch = createMemo(() => newPassword() === confirmPassword() && newPassword().length >= 8); @@ -54,7 +54,8 @@ export default function ForgotPasswordRoute() { body: JSON.stringify({ email: email().trim().toLowerCase() }), }); if (res.ok) { - setSuccess('If an account exists with this email, a reset link has been sent.'); + setSuccess('If an account exists with this email, a 6-digit reset code has been sent.'); + setStep('reset'); } else { setError('Unable to process request. Please try again.'); } @@ -68,8 +69,8 @@ export default function ForgotPasswordRoute() { const resetPassword = async () => { setError(''); setSuccess(''); - if (!token()) { - setError('Reset token is missing. Use the link from your email.'); + if (!code().trim()) { + setError('Enter the reset code from your email.'); return; } if (newPassword().length < 8) { @@ -86,7 +87,7 @@ export default function ForgotPasswordRoute() { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - token: token(), + code: code().trim(), new_password: newPassword(), }), }); @@ -95,7 +96,7 @@ export default function ForgotPasswordRoute() { setSuccess('Password reset successfully. You can now login.'); setTimeout(() => navigate('/login', { replace: true }), 2000); } else { - setError(String(data?.message || data?.error || 'Reset failed. Token may be expired.')); + setError(String(data?.message || data?.error || 'Reset failed. Code may be invalid or expired.')); } } catch { setError('Network error. Please check your connection.'); @@ -124,7 +125,21 @@ export default function ForgotPasswordRoute() {

Set New Password

-

Enter your new password below.

+

Enter the 6-digit code sent to your email and choose a new password.

+ +
+ + setCode(e.currentTarget.value.replace(/\D/g, '').slice(0, 6))} + placeholder="Enter 6-digit code" + /> +
@@ -157,12 +172,24 @@ export default function ForgotPasswordRoute() { }>

Forgot Password

-

Enter your email to receive a password reset link.

+

Enter your email to receive a 6-digit password reset code.

@@ -173,7 +200,7 @@ export default function ForgotPasswordRoute() {