Skip to content

ADR-0004. OTP state in Redis (TTL-based), not in PostgreSQL

FieldValue
StatusAccepted
Date2026-02-12
Decidersidentity-team
Supersedes

Context

  • OTP flows have transient state with strict TTL semantics:
    • Hashed code (10-minute TTL)
    • Attempt counter (lockout after 5 attempts)
    • Cooldown after each send (60s)
    • Daily quota counter
  • Storing in PostgreSQL means: TTL via cron-like cleanup, locking on counter increments, table bloat from millions of expired rows.

Decision

All OTP runtime state lives in Redis with key namespacing:

KeyTTLPurpose
{ns}:otp:{identifier}5–15 minHashed code
{ns}:lock:{identifier}10–15 minAccount lockout
{ns}:session:{token}1–24 hVerified-session token
{ns}:cooldown:{identifier}60sResend cooldown
{ns}:daily:{identifier}24 hDaily quota

Namespaces: verify-email, verify-phone, forgot-password, phone-auth, add-phone, add-email.

Codes are hashed with Bun.password before storage.

Consequences

ProsCons
TTL-based expiry — no cleanup job neededOTP flows hard-fail if Redis is down
Atomic INCR for countersState is ephemeral — lost on Redis flush
Sub-millisecond readsCode hashing adds CPU on every verify
No row-bloat in PostgreSQLAudit trail of OTP attempts not persisted

Failure mode

If Redis is unavailable:

  • New OTP requests return 503 Service Unavailable
  • Existing JWT verification still works (cache is at sister side)
  • Previously-issued OTP tokens still verify if cached client-side, but verification fails server-side

Alternatives Considered

OptionProsConsWhy rejected
PostgreSQL with cron cleanupAudit trailLock contention on counters; cleanup job latency; bloatWrong tradeoff
In-memory only (per-instance)FastestDoesn't survive restarts; doesn't scale to N podsOps fragility
Hybrid (DB write + Redis cache)Audit + speedDouble-write coordinationComplexity not worth it

References

  • BaseOTPBasedMFAService (abstract base)
  • EmailOtpService, PhoneOtpService (concrete)
  • BindingKeys.APPLICATION_REDIS_BULLMQ

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.