NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
  • Encrypted secret at rest — the TOTP secret is stored through Laravel’s encrypted cast (your APP_KEY). Recovery codes are hashed one-way by default.
  • Hidden from serialization — the secret, recovery-code and last-used-timestep columns are pushed into $hidden, so return $user; never leaks them. two_factor_confirmed_at stays visible for UI state.
  • Constant-time compare — every code comparison goes through crypto-for-laravel’s ConstantTime; the package never uses ===, md5 or sha1 for comparisons.
  • Atomic replay protection — the matched timestep is claimed in a single check-and-set, including the code that confirmed enrolment.
  • Atomic single-use recovery codes — consumption re-reads the row under a transaction lock, so a code phished once cannot be raced through twice.
  • Pending enrolments never pass — only a confirmed second factor can satisfy a challenge.
  • Built-in brute-force limiter — on by default. Each attempt is counted atomically before it is verified, so parallel requests can’t slip extra guesses past the limit. Set attempts to null to run your own throttle middleware.
  • Bounds-checked config — digits, period, window, secret_length and attempts are validated at runtime; a misconfiguration throws instead of silently degrading to weak 2FA.
  • #[SensitiveParameter] on every secret and code argument keeps them out of stack traces; the package never logs secrets or codes.

No home-grown crypto

HOTP/TOTP (RFC 4226 / 6238), the otpauth:// URI, the strict RFC 4648 base32 codec, the CSPRNG secret generator and the constant-time comparison all come from our crypto-for-laravel. Nothing cryptographic is re-implemented here — an architecture test enforces it. This package owns the Laravel-side ceremony: enrolment, confirmation, replay guards, recovery codes, rate limiting and the encrypted-at-rest columns.

Hashed vs. encrypted recovery codes

Hashed (the default) means a database dump plus a leaked APP_KEY never yields live recovery codes. Encrypted keeps the codes displayable again, at the cost of being reversible with APP_KEY — see Recovery codes before switching.

What it leaves to you

The package deliberately does not draw QR images, ship login screens, routes or cookies, or mint and verify session or access tokens. It owns the two-factor secret, the codes and the recovery codes — use its events for notifications and audit logging.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.