72 lines
2.8 KiB
Markdown
72 lines
2.8 KiB
Markdown
# 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
|
|
|
|
1. User visits `/ims/login`, clicks "Forgot password?" link
|
|
2. Redirected to `/ims/forgot-password`
|
|
3. Enters email → form calls `supabase.auth.resetPasswordForEmail(email, { redirectTo })`
|
|
4. Supabase sends reset email with a link containing a one-time code
|
|
5. User clicks link → browser hits `/ims/api/auth/callback?code=XXX&next=/auth/reset-password`
|
|
6. Existing callback exchanges code, creates recovery session, redirects to `/ims/auth/reset-password`
|
|
7. User enters new password + confirmation → `supabase.auth.updateUser({ password })`
|
|
8. On success, redirect to `/ims/login` with 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 form
|
|
- `app/(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-sm` centered 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)
|