Estimate: 22m · Depends on: 1.1.2, 1.1.3
Implement the backend half of password reset (the token table, the "request reset" handler that sends an email with a one-time link, and the "set new password" handler that consumes the token) and introduce a small email-provider abstraction (lib/email.ts) so the rest of the codebase never depends on a specific mailer. The UI half lives in 1.1.5; this subtask provides the endpoints those pages call plus the email abstraction those endpoints (and the email-verification flow) use.
Why an abstraction, not a direct Resend/Postmark/SES call: Per the planner-as-consumer principle (MOTIR.md "Current state"), production email-provider choice is planner work — each Motir-planned project's planner decides which provider to use in pre-plan and adds a mandatory Story to wire it. The starter (and motir-core itself for v1) ships only the abstraction + a dev console-logging provider that prints reset links to stdout. Production wiring is deferred. This keeps the starter dependency-free of any specific email vendor while making the wiring point explicit (lib/email.ts's sendEmail() export).
Why split from 1.1.5: Reset is a security-sensitive flow (timing attacks, token reuse, account enumeration) that benefits from focused review independent of the UI. Splitting also lets it run in parallel with 1.1.5 since they share no files.
What you'll do: Add a PasswordResetToken table (token-hash, user-id, expires-at, used-at). Create /lib/email.ts exporting sendEmail({ to, subject, html, text? }) as the canonical interface; provide a DevConsoleEmailProvider implementation that logs to stdout with a clear [EMAIL] marker; wire the production-provider hook as a single env-var switch (EMAIL_PROVIDER=console default; future values resend, postmark, etc.). Create Server Actions requestReset(email) and confirmReset(token, newPassword) in /app/(auth)/reset-password/actions.ts. Tokens expire in 1 hour and are single-use.
PasswordResetToken table with required fields + indexes on userId and expiresAt./lib/email.ts exports a typed sendEmail({ to, subject, html, text? }) → Promise<void> interface and a DevConsoleEmailProvider implementation. Selected via EMAIL_PROVIDER env var (default: console); unknown values throw a clear startup error.[EMAIL] To: ... Subject: ... Body: ... with the full reset link visible, so dev/test flows can extract it from stdout.requestReset(email): rate-limit to 3 requests/hour/email; do NOT reveal whether the email exists in the response; calls sendEmail with the reset link only if a user is found (silent no-op otherwise).confirmReset(token, newPassword): validates token, checks expiry, checks unused, updates password via the repo from 1.1.3, marks token used, returns success or typed error..env.example documents EMAIL_PROVIDER with the comment "console for dev; production provider wiring is planner work — see your project's pre-plan email-provider Story."sendEmail with the console provider writes the expected marker to stdout./lib/users/repo.ts — user repository (from 1.1.3)/lib/auth/passwords.ts — hashing helper (from 1.1.3)/prisma/schema.prisma — current schema