Install the Package
composer require danielburger1337/2fa-email
Ensure scheb/2fa-bundle is also installed (this package extends it).
Enable the Bundle
Add to config/bundles.php:
return [
// ...
Scheb\TwoFactorBundle\SchebTwoFactorBundle::class => ['all' => true],
DanielBurger\TwoFactorEmailBundle\DanielBurgerTwoFactorEmailBundle::class => ['all' => true],
];
Configure the Bundle Publish the default config:
php artisan vendor:publish --provider="DanielBurger\TwoFactorEmailBundle\DanielBurgerTwoFactorEmailBundle" --tag="config"
Update config/scheb_two_factor.yaml to use the email provider:
scheb_two_factor:
provider:
name: danielburger_email
First Use Case: Enabling 2FA for a User Trigger the 2FA flow in your login/registration logic:
use Scheb\TwoFactorBundle\Model\TwoFactorInterface;
$user = $this->getUser();
if ($user instanceof TwoFactorInterface && !$user->isTwoFactorEnabled()) {
$user->enableTwoFactor();
$user->generateTwoFactorCode(); // Generates and sends email
}
Enable 2FA
$user->enableTwoFactor(); // Sets `is_two_factor_enabled` to true
$user->generateTwoFactorCode(); // Triggers email with code
Verify Code
$form = $this->createFormBuilder($user)
->add('twoFactorCode', TextType::class)
->getForm();
if ($form->isSubmitted() && $form->isValid()) {
$code = $form->get('twoFactorCode')->getData();
if ($user->verifyTwoFactorCode($code)) {
// Redirect to dashboard or set flash message
}
}
Disable 2FA
$user->disableTwoFactor();
Customize Email Templates Override the default email template by publishing assets:
php artisan vendor:publish --provider="DanielBurger\TwoFactorEmailBundle\DanielBurgerTwoFactorEmailBundle" --tag="emails"
Update resources/views/vendor/danielburger_two_factor_email/email.txt.twig or .html.twig.
Extend the Provider
Create a custom provider by extending DanielBurger\TwoFactorEmailBundle\Provider\EmailProvider:
namespace App\TwoFactor;
use DanielBurger\TwoFactorEmailBundle\Provider\EmailProvider as BaseEmailProvider;
class CustomEmailProvider extends BaseEmailProvider {
public function getEmailTemplate(): string {
return '@app/emails/2fa_custom.html.twig';
}
}
Register it in config/scheb_two_factor.yaml:
scheb_two_factor:
provider:
name: custom_email
class: App\TwoFactor\CustomEmailProvider
Handle Expiry The default expiry is 15 minutes. Adjust in config:
scheb_two_factor:
danielburger_email:
code_expiry: 300 # 5 minutes in seconds
Queue Emails (Optional) Use Laravel queues to defer email sending:
$user->generateTwoFactorCode(); // Uses Mailer (queued if configured)
Ensure MAIL_MAILER is set to queue in .env.
Email Not Sending?
MAIL_MAILER is configured in .env (e.g., MAIL_MAILER=smtp).Code Expiry Too Short/Long
code_expiry in config if needed.php artisan tinker:
$user = App\Models\User::first();
$user->generateTwoFactorCode();
sleep(900); // 15 minutes
$user->verifyTwoFactorCode('123456'); // Should fail
Template Overrides Not Working
resources/views/vendor/danielburger_two_factor_email/.php artisan view:clear
Race Conditions on Code Verification
Log Code Generation Temporarily log codes for debugging (remove in production):
// In a service provider or controller
\Log::info('2FA Code for ' . $user->email, ['code' => $user->getTwoFactorCode()]);
Check User Model
Ensure your User model implements Scheb\TwoFactorBundle\Model\TwoFactorInterface:
use Scheb\TwoFactorBundle\Model\TwoFactorInterface;
class User extends Authenticatable implements TwoFactorInterface {
// ...
}
Customize Email Content
Override the getEmailTemplate() method in a custom provider (as shown above) or extend the Twig template.
Add Rate Limiting Prevent brute-force attacks by limiting code attempts:
// In a middleware or controller
if ($user->getFailedTwoFactorAttempts() >= 5) {
abort(429, 'Too many attempts. Try again later.');
}
Localization
The email template supports translation. Add translations to resources/lang/{locale}/validation.php or create a custom translation file for 2FA terms.
Event Listeners Listen for 2FA events to log or notify admins:
// In a service provider
$this->app->booted(function () {
\Event::listen(\Scheb\TwoFactorBundle\Event\TwoFactorCodeGeneratedEvent::class, function ($event) {
\Log::info('2FA code generated for ' . $event->getUser()->email);
});
});
Provider Naming
The config key for this provider is danielburger_email, not email. Ensure you reference it correctly:
scheb_two_factor:
danielburger_email: # Correct key
code_expiry: 300
Fallback Provider If the email provider fails (e.g., no email configured), the bundle does not fall back to another provider. Handle this gracefully in your logic:
try {
$user->generateTwoFactorCode();
} catch (\Exception $e) {
// Fallback to SMS or show an error
}
How can I help you explore Laravel packages today?