feat(auth): add forgot password flow, password toggle, and error differentiation (#700)#727
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Bundle Size Report
✅ Bundle size is within acceptable limits. |
There was a problem hiding this comment.
Pull request overview
This PR implements three auth UX improvements for the login flow: a forgot/reset password flow, a password visibility toggle, and error message differentiation via i18n keys.
Changes:
- Adds a forgot password page (
ForgotPasswordForm) that callsresetPasswordForEmail()with an always-show-success pattern for security, and an update password page (UpdatePasswordForm) with new/confirm fields and client-side mismatch validation - Adds an Eye/EyeOff toggle button to all password inputs (login + update password) with accessible
aria-labelswitching - Introduces
classifyAuthError()to map Supabase error messages to i18n keys (auth.invalidCredentials/auth.tooManyAttempts) instead of showing raw error strings
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
frontend/src/app/auth/login/LoginForm.tsx |
Added password toggle, "Forgot password?" link, and classifyAuthError() for i18n error mapping |
frontend/src/app/auth/login/LoginForm.test.tsx |
Added i18n mock and 4 new tests (invalidCredentials, tooManyAttempts, toggle, forgot link) |
frontend/src/app/auth/forgot-password/page.tsx |
New page wrapper with Suspense/LoadingSpinner |
frontend/src/app/auth/forgot-password/ForgotPasswordForm.tsx |
New form component for requesting password reset email |
frontend/src/app/auth/forgot-password/ForgotPasswordForm.test.tsx |
6 tests covering rendering, submission, success state, loading |
frontend/src/app/auth/update-password/page.tsx |
New page wrapper with Suspense/LoadingSpinner |
frontend/src/app/auth/update-password/UpdatePasswordForm.tsx |
New form for setting new password after recovery |
frontend/src/app/auth/update-password/UpdatePasswordForm.test.tsx |
9 tests covering mismatch, success, error, toggle, loading |
frontend/src/app/auth/callback/route.ts |
Added type=recovery detection to redirect to update-password page |
frontend/messages/en.json |
20 new auth i18n keys for the forgot/update password flows |
frontend/messages/pl.json |
Polish translations for the 20 new auth keys |
frontend/messages/de.json |
German translations for the 20 new auth keys |
| setLoading(false); | ||
|
|
||
| if (error) { | ||
| showToast({ type: "error", message: error.message }); |
There was a problem hiding this comment.
This uses raw message: error.message to display Supabase error strings directly to the user. This is inconsistent with the PR's own error differentiation pattern introduced in LoginForm.tsx (which maps errors to i18n keys via classifyAuthError). Raw Supabase messages are in English and won't be localized for PL/DE users. Consider using a messageKey with an i18n key (e.g., auth.passwordUpdateFailed) for a generic user-friendly error message, similar to how LoginForm handles it.
| showToast({ type: "error", message: error.message }); | |
| showToast({ type: "error", messageKey: "auth.passwordUpdateFailed" }); |
| // After confirming email, go to onboarding (app layout will check) | ||
| return NextResponse.redirect(new URL("/app/search", request.url)); | ||
| const type = searchParams.get("type"); | ||
| const destination = type === "recovery" ? "/auth/update-password" : "/app/search"; |
There was a problem hiding this comment.
The comment "After confirming email, go to onboarding (app layout will check)" is now stale since this code also handles password recovery redirects. It should be updated to reflect both use cases (e.g., "Redirect based on callback type: recovery goes to password update, all others to app").
…iolation
getByLabel('Password') matched both the password input (via <label for>)
and the 'Show password' toggle button (substring match on aria-label).
Adding { exact: true } ensures only the exact label text 'Password' is
matched, fixing the Playwright strict mode error introduced by the
password toggle feature in PR #727.
12 occurrences fixed across 6 E2E test files.
…iolation (#752) getByLabel('Password') matched both the password input (via <label for>) and the 'Show password' toggle button (substring match on aria-label). Adding { exact: true } ensures only the exact label text 'Password' is matched, fixing the Playwright strict mode error introduced by the password toggle feature in PR #727. 12 occurrences fixed across 6 E2E test files. Co-authored-by: ericsocrat <ericsocrat@users.noreply.github.com>
Summary
Closes #700
Adds three auth UX improvements to the login flow:
1. Forgot Password Flow
/auth/forgot-passwordpage withForgotPasswordForm— email input,resetPasswordForEmail()call, always-show-success pattern (security)/auth/update-passwordpage withUpdatePasswordForm— new + confirm password fields, independent visibility toggles, client-side mismatch validation,updateUser()call/auth/callback/route.tsto detecttype=recoveryand redirect to/auth/update-password2. Password Visibility Toggle
aria-labelswitches betweenShow password/Hide password3. Error Differentiation
classifyAuthError()maps Supabase error messages to i18n keys:auth.tooManyAttemptsauth.invalidCredentialsmessageKeyinstead of rawmessageFiles Changed
12 files changed, +834 / -21 lines
Tests