Storage Guidance#
Secrets#
OTP secrets are reversible secrets. If your application must generate future codes, hashing alone is not enough.
Recommended practice:
store OTP secrets encrypted at rest
keep encryption keys outside the primary application database when possible
version secret records so rotation and grace-period overlap can be tracked safely
prefer a separate secret reference in enrollment records instead of duplicating raw secrets everywhere
The package includes Infocyph\OTP\Contracts\SecretStoreInterface for applications that want an explicit abstraction around secret persistence.
Recovery codes#
Recovery codes should usually be stored hashed.
Recommended practice:
hash each recovery code before storage
store one row per code or per active batch plus child rows
track
used_ator equivalent consumption state atomicallyrevoke or replace the old active batch when issuing a new set
expose derived metadata such as total issued, remaining, and last used
Replay state#
Replay state can live in cache or a database depending on your durability needs.
Recommended practice:
use durable storage for HOTP counters and any state that must survive restarts
use token-expiry or cleanup jobs for consumed TOTP/OCRA replay tokens
index by namespace, binding, and token
keep replay data logically separate from secrets and recovery-code hashes
Generic OTP#
Generic OTP requires a caller-provided PSR-6 cache pool implementation.
Contracts#
The package includes contracts you can implement in your own infrastructure:
Infocyph\OTP\Contracts\ReplayStoreInterfaceInfocyph\OTP\Contracts\RecoveryCodeStoreInterfaceInfocyph\OTP\Contracts\SecretStoreInterface
Implementation examples#
See Custom Persistent Stores for a step-by-step guide to building persistent database-backed implementations for:
recovery code tracking
replay tracking
secret storage design guidance
day-to-day metadata such as total issued, remaining, and last-used time