2.8 KiB
Forgot Password Flow — Design Spec
Date: 2026-07-21
Status: Approved
Overview
Add a self-service password reset flow so users can recover access without admin intervention. Uses Supabase Auth's built-in resetPasswordForEmail + PKCE code exchange via the existing /api/auth/callback route.
User Flow
- User visits
/ims/login, clicks "Forgot password?" link - Redirected to
/ims/forgot-password - Enters email → form calls
supabase.auth.resetPasswordForEmail(email, { redirectTo }) - Supabase sends reset email with a link containing a one-time code
- User clicks link → browser hits
/ims/api/auth/callback?code=XXX&next=/auth/reset-password - Existing callback exchanges code, creates recovery session, redirects to
/ims/auth/reset-password - User enters new password + confirmation →
supabase.auth.updateUser({ password }) - On success, redirect to
/ims/loginwith success message
Files Changed
Modified
components/auth/login-form.tsx— add "Forgot password?" link below the Sign in button
New
app/(auth)/forgot-password/page.tsx— public page, email input formapp/(auth)/reset-password/page.tsx— public page (but requires recovery session), new password form
Architecture
Both new pages live in the (auth) route group (public, no middleware auth redirect). The reset-password page is technically public but only functional with a valid Supabase recovery session in the browser cookie — without it, updateUser returns an error and the form shows a graceful message.
No new API routes needed. No changes to /api/auth/callback.
Redirect URL
resetPasswordForEmail redirectTo value:
${process.env.NEXT_PUBLIC_APP_URL}/api/auth/callback?next=/auth/reset-password
This must be added to Supabase Auth → URL Configuration → Redirect URLs:
http://64.176.82.100/ims/api/auth/callback
(likely already present from existing auth setup — verify before deploying)
Validation
Forgot password form:
- Email must be non-empty and valid format (client-side only)
- On submit: always show success message regardless of whether email exists (prevents user enumeration)
Reset password form:
- Min 8 characters
- New password and confirm must match
- On Supabase error (expired/invalid session): show "Link expired — request a new one" with link back to
/forgot-password
UI
Matches existing login form styling:
max-w-smcentered card- Same border/rounded/focus Tailwind classes as
components/auth/login-form.tsx - Same error/success message pattern (
bg-red-50 border border-red-200 text-red-700/ green equivalent)
Out of Scope
- Email template customisation (handled in Supabase dashboard)
- Rate limiting (Supabase enforces this on
resetPasswordForEmail) - Admin-triggered reset (already possible via Supabase dashboard)