scheb/2fa-backup-code
Adds backup code support to scheb/2fa for Symfony apps. Generate, store, and validate one-time recovery codes so users can access accounts when they lose their 2FA device. Integrates with existing 2FA flows and user providers.
Installation Add the package via Composer:
composer require scheb/2fa-backup-code
Ensure scheb/2fa-bundle is also installed (this package extends it).
Configuration Publish the config file:
php artisan vendor:publish --provider="Scheb\TwoFactorBundle\SchebTwoFactorBundle" --tag=config
Update config/scheb_two_factor.php to enable backup codes:
'backup_codes' => [
'enabled' => true,
'count' => 10, // Default number of backup codes per user
],
First Use Case Trigger backup code generation for a user during registration or profile setup:
use Scheb\TwoFactorBundle\Model\BackupCodeManagerInterface;
$backupCodeManager = $this->get('scheb_two_factor.backup_code_manager');
$backupCodes = $backupCodeManager->generateBackupCodes($user);
$backupCodeManager->saveBackupCodes($user, $backupCodes);
Generate & Store Codes
// Generate and save codes in a single step
$backupCodeManager->generateAndSaveBackupCodes($user);
Display to User Render the codes in a secure UI (e.g., masked or QR-encoded):
{% for code in user.backupCodes %}
{{ code.code }} {# Display or mask as needed #}
{% endfor %}
Revoke Codes
$backupCodeManager->revokeBackupCodes($user);
Backup Code Validation Extend the login logic to accept backup codes when 2FA fails:
use Scheb\TwoFactorBundle\Security\TwoFactorAuthenticator;
$authenticator = $this->get(TwoFactorAuthenticator::class);
if (!$authenticator->authenticate($request, $user)) {
$backupCode = $request->input('backup_code');
if ($backupCodeManager->consumeBackupCode($user, $backupCode)) {
// Grant access
}
}
Event Listeners
Listen for scheb_two_factor.backup_code_generated to log or notify admins:
public function handleBackupCodeGenerated(BackupCodeGeneratedEvent $event) {
\Log::info("Backup codes generated for user: {$event->user->id}");
}
BackupCodeStorageInterface:
class CustomBackupCodeStorage implements BackupCodeStorageInterface {
public function save($user, array $codes) { /* ... */ }
public function load($user) { /* ... */ }
public function consume($user, $code) { /* ... */ }
}
Register it in config/scheb_two_factor.php:
'backup_codes' => [
'storage' => CustomBackupCodeStorage::class,
],
Backup Code Consumption
BackupCodeStorageInterface to customize behavior (e.g., allow reuse).Security Risks
****-****-1234).Database Migrations
backup_codes table. If using a custom storage, ensure migrations align with your schema.Missing Backup Codes
Verify the backup_codes config is enabled and the storage service is bound:
php artisan config:clear
php artisan cache:clear
Codes Not Consuming Check for:
Custom Code Generation Override the default generator (e.g., for alphanumeric codes):
$backupCodeManager->setBackupCodeGenerator(new CustomBackupCodeGenerator());
Rate Limiting Limit backup code consumption attempts to prevent brute force:
$backupCodeManager->setRateLimiter(new BackupCodeRateLimiter(5, 60)); // 5 attempts/minute
Multi-Factor Backup Combine with other backup methods (e.g., email recovery) by extending the authenticator:
class MultiFactorAuthenticator extends TwoFactorAuthenticator {
public function authenticate(Request $request, UserInterface $user) {
if ($this->isBackupCodeRequest($request)) {
return $this->authenticateWithBackupCode($request, $user);
}
return parent::authenticate($request, $user);
}
}
How can I help you explore Laravel packages today?