Password Recovery

SecurityRecovery flowCode lifecycle

Password recovery is implemented as a code-based flow backed by Prisma and email delivery.

The flow is designed to balance usability with control by applying expiration, throttling, and one-time recovery rules through dedicated router procedures instead of spreading that logic across UI code.

Related docs

For the broader authentication architecture, see Authentication and Configure Auth. This page focuses on the recovery flow itself as a security boundary.

What this security area covers

Recovery issuance

The system generates codes and controls their lifetime and resend behavior through the customAuth router procedures.

Verification flow

Passwords can only be updated after the user submits a valid recovery code and a new password.

Reusable safeguards

Expiration, throttling, and record consumption stay centralized in the router so the security rules remain testable and consistent.

Main files

  • src/server/api/routers/customAuth.ts
Current safeguards

Current behavior

The customAuth.ts router procedures currently provide:

  • six-digit code generation
  • 15-minute expiration window
  • resend throttling
  • code lookup and consumption

These behaviors make the recovery flow predictable and reduce the risk of unlimited retries or stale code reuse.

Security properties

  • codes expire automatically
  • only one active code is kept per user
  • resend attempts are rate-limited
  • password changes only happen after code verification

Flow summary

  1. User requests a recovery email
  2. The app finds the user and rejects unsupported cases such as Google-only accounts
  3. A code is issued and emailed
  4. The user submits the code and a new password
  5. The app verifies the code, hashes the password, updates the user, and deletes the recovery record

Security boundary

Keep throttling, code lifetime, and recovery record consumption inside the customAuth.ts router procedures so the same safeguards apply no matter how the route or UI evolves.

If you evolve this flow further, keep the throttling and expiration behavior in the customAuth.ts procedures so it stays consistent and testable.