Files
ims/docs/superpowers/specs/2026-07-21-forgot-password-design.md

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

  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)