Every event lives in RoundlyConsulting\TwoFactor\Events and carries the user model as $user — never a secret or a code. A few add a non-sensitive detail:
| Event | Fired when | Payload |
|---|---|---|
TwoFactorEnrolmentStarted | start() persists a fresh pending secret. | user |
TwoFactorConfirmed | confirm() enables 2FA. | user |
TwoFactorDisabled | disable() clears all two-factor state. | user |
RecoveryCodesRegenerated | recoveryCodes()->regenerate() replaces the code set. | user |
RecoveryCodeConsumed | attempt() burns a recovery code; remaining is the count left. | user, remaining |
TwoFactorVerified | A user passes a challenge, via TwoFactorMethod::Totp or ::RecoveryCode. | user, method |
TwoFactorVerificationFailed | An enrolled user fails a challenge — not on a replay, not for a user without a secret. | user |
TwoFactorReplayDetected | A code whose timestep was already used is rejected. | user, timestep |
TwoFactorRateLimited | The built-in limiter locks a user out. | user, secondsUntilAvailable |
Listening
TwoFactorVerified and TwoFactorVerificationFailed let you audit challenges, meter success and failure rates, and drive host-side lockout without wrapping the facade — for example a listener that increments a rate limiter:
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\RateLimiter;
use RoundlyConsulting\TwoFactor\Events\TwoFactorVerificationFailed;
Event::listen(function (TwoFactorVerificationFailed $event): void {
RateLimiter::hit("2fa:{$event->user->getKey()}");
});Enrolment, disabling, recovery-code use and replay attempts are all announced too, so you can feed them into an audit log or alerting without extra plumbing.
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 cryptoBy 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.