Skip to content

fix(auth): wire the full reset-password flow (#151)#171

Merged
gkhngyk merged 2 commits into
mainfrom
fix/reset-password-flow
Jun 4, 2026
Merged

fix(auth): wire the full reset-password flow (#151)#171
gkhngyk merged 2 commits into
mainfrom
fix/reset-password-flow

Conversation

@gkhngyk

@gkhngyk gkhngyk commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #151. Password recovery was broken — the Forgot Password link on the sign-in page was a dead href="#" placeholder, and there were no recovery routes. This builds the standard Supabase password-reset flow end-to-end.

  • /forgot-password — single email input that calls supabase.auth.resetPasswordForEmail(email, { redirectTo: \${APP_URL}/auth/confirm?next=/reset-password` })`. Always shows a generic "If that email exists, we've sent a reset link" message regardless of email validity, to prevent account enumeration.
  • /reset-password — new password + confirm inputs that call supabase.auth.updateUser({ password }). The session is already established by the shared /auth/confirm route (same verifyOtp path that powers invite acceptance, see fix(invite): switch to verifyOtp + /auth/confirm — invite mails use hash-flow, not PKCE #129), so updateUser works directly. Signs the user out and redirects to /sign-in on success.
  • Wired the link in sign-in-form.tsx from href="#" to <Link href="/forgot-password">.
  • i18n keys added for both flows + errors.passwordMismatch.

Manual config (done outside the repo)

  • Supabase Dashboard → Authentication → Emails → Reset Password template link switched to {{ .RedirectTo }}&token_hash={{ .TokenHash }}&type=recovery.
  • NEXT_PUBLIC_APP_URL confirmed set (used by redirectTo).

Test plan

  • Click Forgot Password on /sign-in → lands on /forgot-password
  • Submit an email → shows the generic "we sent a link if the account exists" message (same for registered + unregistered emails)
  • Reset email arrives via Resend (check Resend Logs)
  • Click the email link → lands on /reset-password with an active session (no /sign-up detour)
  • Set a new password → sign out + sign back in with the new password succeeds
  • No regression on the invite-accept flow at /invite/<token> (shared /auth/confirm route)
  • tsc --noEmit + eslint clean (0 errors)

Made with Cursor

Password recovery was broken — the Forgot Password link was a dead
placeholder with no recovery routes. This builds the standard Supabase
reset flow end-to-end:

- /forgot-password: email input calling resetPasswordForEmail, always
  shows a generic "if that email exists" message to prevent account
  enumeration
- /reset-password: new password + confirm inputs calling updateUser,
  using the session established by the shared /auth/confirm route
- Wire the sign-in Forgot Password link to /forgot-password
- Add i18n keys for both flows

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@gkhngyk gkhngyk merged commit 289847a into main Jun 4, 2026
3 checks passed
@gkhngyk gkhngyk deleted the fix/reset-password-flow branch June 4, 2026 13:11
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.

fix(auth): Forgot Password link is dead — wire the full reset-password flow

1 participant