Skip to content

feat(auth): add forgot password flow, password toggle, and error differentiation (#700)#727

Merged
ericsocrat merged 2 commits intomainfrom
feat/700-login-improvements
Mar 8, 2026
Merged

feat(auth): add forgot password flow, password toggle, and error differentiation (#700)#727
ericsocrat merged 2 commits intomainfrom
feat/700-login-improvements

Conversation

@ericsocrat
Copy link
Owner

Summary

Closes #700

Adds three auth UX improvements to the login flow:

1. Forgot Password Flow

  • New /auth/forgot-password page with ForgotPasswordForm — email input, resetPasswordForEmail() call, always-show-success pattern (security)
  • New /auth/update-password page with UpdatePasswordForm — new + confirm password fields, independent visibility toggles, client-side mismatch validation, updateUser() call
  • Updated /auth/callback/route.ts to detect type=recovery and redirect to /auth/update-password

2. Password Visibility Toggle

  • Added Eye/EyeOff toggle button to login password field
  • Accessible: aria-label switches between Show password / Hide password

3. Error Differentiation

  • classifyAuthError() maps Supabase error messages to i18n keys:
    • Rate-limit errors -> auth.tooManyAttempts
    • All others -> auth.invalidCredentials
  • Error toast now uses messageKey instead of raw message

Files Changed

12 files changed, +834 / -21 lines

  • 3 i18n files: 20 new auth keys each (en, pl, de)
  • 6 new files: 2 form components + 2 pages + 2 test files
  • 3 modified: LoginForm.tsx, LoginForm.test.tsx, callback/route.ts

Tests

  • 28 tests across 3 test files (13 LoginForm + 6 ForgotPassword + 9 UpdatePassword)
  • 4 new LoginForm tests: invalidCredentials key, tooManyAttempts key, password toggle, forgot link
  • Full suite: 307 passed, 0 failed (5136 tests total)
  • TypeScript: 0 errors

Copilot AI review requested due to automatic review settings March 7, 2026 15:33
@vercel
Copy link

vercel bot commented Mar 7, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tryvit Ready Ready Preview, Comment Mar 8, 2026 8:50am

@github-actions
Copy link

github-actions bot commented Mar 7, 2026

Bundle Size Report

Metric Value
Main baseline 3444 KB
This PR 3464 KB
Delta +20 KB (+0.6%)
JS chunks 135
Hard limit 4000 KB

✅ Bundle size is within acceptable limits.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 calls resetPasswordForEmail() 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-label switching
  • 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 });
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
showToast({ type: "error", message: error.message });
showToast({ type: "error", messageKey: "auth.passwordUpdateFailed" });

Copilot uses AI. Check for mistakes.
Comment on lines 35 to +37
// 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";
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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").

Copilot uses AI. Check for mistakes.
@ericsocrat ericsocrat merged commit 50fef49 into main Mar 8, 2026
16 of 17 checks passed
@ericsocrat ericsocrat deleted the feat/700-login-improvements branch March 8, 2026 09:01
ericsocrat added a commit that referenced this pull request Mar 8, 2026
…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.
ericsocrat added a commit that referenced this pull request Mar 8, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(frontend): login page — add forgot password flow, password toggle, and error differentiation

2 participants