Leave Localhost logoLeave LocalhostDocs
Security

Email Verification Codes

A 6-digit, single-use, hashed, rate-limited email code that clears a sensitive-action step-up for any account type — the universal verification fallback.

Email verification codes are the universal fallback among the three sensitive-action verification methods. Because they only require a verified email, they work for every account type, including OAuth-only and magic-link users who have no password to confirm.

How it works

  1. The user requests a code for a specific action. The backend (security/challenges.ts) creates a challenge and emails a 6-digit code.
  2. The code is single-use and short-lived. Only a salted, peppered SHA-256 hash is stored — never the plaintext code (security/crypto.ts, peppered with BETTER_AUTH_SECRET).
  3. The user enters the code; the backend hashes the input and compares. On a match it consumes the challenge and mints the action-scoped grant.

The email template lives at email/templates/sensitiveActionVerificationEmail.tsx.

Limits and cooldowns

  • Creation is limited by sensitiveEmailChallengeCreate in rateLimit.ts (3 codes per 10 minutes, keyed per user and action).
  • A 60-second resend cooldown is enforced inline when creating a challenge.
  • Codes are single-use; consuming or expiring one invalidates it.

See Rate Limiting for how these limits are configured.

Why hashing

Storing only a hash means a database read never reveals a usable code, and the audit log never contains one. The same approach protects session scopes used by fresh-session checks.

On this page