Installation
composer require dracoder/s4-2fa
(Note: The package is designed for Symfony, but Laravel users can adapt it via a wrapper or standalone usage.)
Basic Usage
use Dracoder\S4TwoFactor\TwoFactorAuth;
$twoFactor = new TwoFactorAuth();
$secret = $twoFactor->generateSecret(); // Generates a Base32 secret
chillerlan/qr-code for Laravel):
$qrCodeUrl = $twoFactor->getQRCodeUrl('issuer', '[email protected]', $secret);
$isValid = $twoFactor->verifyCode($secret, '123456');
First Use Case
$secret in the user model (e.g., user_2fa_secret column).User Initiates Setup
Code Verification
if ($twoFactor->verifyCode($secret, $userInput)) {
$user->enableTwoFactor(); // Custom logic to mark user as 2FA-enabled
}
Login Flow
if ($user->isTwoFactorEnabled()) {
$code = request('two_factor_code');
if (!$twoFactor->verifyCode($user->twoFactorSecret, $code)) {
return back()->withErrors(['2fa' => 'Invalid code']);
}
}
Database Storage
Use Laravel’s encrypt() for the secret:
$user->two_factor_secret = encrypt($secret);
$user->save();
Retrieve with decrypt($user->two_factor_secret).
Caching TOTP Codes The package uses a 30-second window for validation. Cache verification results briefly to avoid rate-limiting:
Cache::put("2fa_{$user->id}", true, now()->addSeconds(30));
Fallback Recovery
Store backup codes (manually generated) in user_backup_codes (JSON-encoded array). Clear after use.
Middleware
Create a VerifyTwoFactor middleware to protect routes:
public function handle($request, Closure $next) {
if ($request->user()->isTwoFactorEnabled() && !$request->session()->has('2fa_verified')) {
return redirect()->route('2fa.verify');
}
return $next($request);
}
Time Synchronization
Carbon or DateTime consistently:
$twoFactor->setTimeSource(function() {
return Carbon::now()->getTimestamp();
});
Secret Length
if (strlen($secret) !== 32) {
throw new \InvalidArgumentException('Secret must be 32 characters (Base32)');
}
Rate Limiting
Database Migrations
$table->string('two_factor_secret')->nullable();
$table->json('backup_codes')->nullable();
$table->boolean('two_factor_enabled')->default(false);
Invalid Codes
$twoFactor->verifyCode($secret, $code, 1); // Allow 1 step (15s) for testing.
$expectedCode = $twoFactor->getCurrentCode($secret);
logger("Expected: {$expectedCode}, Got: {$userInput}");
QR Code Issues
$qrCodeUrl = $twoFactor->getQRCodeUrl(
urlencode('MyApp'),
urlencode('[email protected]'),
$secret
);
Custom Algorithms
Override the default TOTP algorithm (SHA1) by extending the TwoFactorAuth class:
class CustomTwoFactorAuth extends TwoFactorAuth {
protected function getHashAlgorithm() {
return 'sha256';
}
}
Event Dispatching
Trigger events for 2FA actions (e.g., TwoFactorEnabled, BackupCodeUsed):
event(new TwoFactorEnabled($user));
Multi-Factor Support Combine with other auth methods (e.g., hardware keys) by extending the verification logic:
public function verifyMultiFactor($secret, $code, $hardwareKey) {
return $this->verifyCode($secret, $code) && $this->validateHardware($hardwareKey);
}
Laravel Service Provider Bind the package to the container for dependency injection:
$this->app->singleton(TwoFactorAuth::class, function() {
$twoFactor = new TwoFactorAuth();
$twoFactor->setTimeSource(fn() => Carbon::now()->getTimestamp());
return $twoFactor;
});
How can I help you explore Laravel packages today?