symfonycasts/verify-email-bundle
Add secure email verification to Symfony apps with signed, expiring links and an easy verification workflow. Includes helpers for generating confirmation URLs, validating requests, and customizing emails and redirects—ideal for registration flows and account security.
Installation
composer require symfonycasts/verify-email-bundle
Add to config/bundles.php:
Symfonycasts\VerifyEmailBundle\VerifyEmailBundle::class => ['all' => true],
Publish Config
php bin/console make:verify-email
This generates:
config/packages/verify_email.yaml (default config)templates/verify_email/ (Twig templates)First Use Case
verified_at column to your users table (migration provided via make:verify-email).VerifyEmailListener in your User entity:
use Symfonycasts\VerifyEmailBundle\Message\SendVerificationEmailMessage;
// In your registration controller/service
$message = new SendVerificationEmailMessage($user);
$this->messageBus->dispatch($message);
config/packages/verify_email.yaml (adjust from_email, success_path, failure_path).templates/verify_email/ (customize email/verification pages).php bin/console verify:email:resend (for testing).// In your registration controller
public function register(Request $request, MessageBusInterface $bus)
{
$user = User::create($request->all());
$bus->dispatch(new SendVerificationEmailMessage($user));
return redirect()->route('verification.notice'); // Default: /verify-email
}
// Resend verification email (e.g., via user profile)
$bus->dispatch(new SendVerificationEmailMessage($user));
// For API responses (e.g., Laravel Sanctum)
return response()->json([
'message' => 'Verification email sent',
'verification_url' => route('verify_email.verify', ['token' => $user->verificationToken])
]);
Service Provider Binding (if not auto-discovered):
// In AppServiceProvider
$this->app->bind(
Symfonycasts\VerifyEmailBundle\Message\SendVerificationEmailMessage::class,
fn() => new SendVerificationEmailMessage($this->app['user'])
);
Route Customization (override defaults):
# config/packages/verify_email.yaml
verify_email:
success_path: '/account/verified' # Replace default
failure_path: '/account/verification-failed'
Queue Integration (for async emails):
// In config/packages/verify_email.yaml
verify_email:
send_email_in_background: true
Unit Test Verification Logic:
$this->bus->expects($this->once())
->method('dispatch')
->with(new SendVerificationEmailMessage($user));
Feature Test Verification Flow:
$response = $this->post('/register', $data);
$response->assertRedirect('/verify-email');
Token Expiration
verify_email.token_ttl).verify_email:
token_ttl: 7200 # 2 hours in seconds
Database Schema Mismatch
Column 'verified_at' not found.make:verify-email:
php bin/console doctrine:migrations:execute --up
Email Delivery Issues
verify_email.send_email_in_background (set to false for debugging).from_email in config matches your SMTP setup.Route Conflicts
Route [verify_email.verify] not defined.config/routes.yaml or web.php).Log Verification Tokens
Add to User entity:
public function getVerificationToken(): ?string
{
return $this->verificationToken;
}
Inspect Queued Jobs
php bin/console debug:queue
Override Templates for Debugging
Copy templates/verify_email/ to your project and add:
{# templates/verify_email/verify_email.html.twig #}
<pre>{{ dump(app('verify_email.token_generator').generateToken($user)) }}</pre>
Custom Token Generator
// config/packages/verify_email.yaml
verify_email:
token_generator: App\Service\CustomTokenGenerator
Event Subscribers
Listen for VerifyEmailEvents:
use Symfonycasts\VerifyEmailBundle\Event\VerificationEmailSentEvent;
public function onEmailSent(VerificationEmailSentEvent $event)
{
// Log or notify (e.g., Slack)
}
API Responses
Extend the VerifyEmailResponse class to customize JSON output:
use Symfonycasts\VerifyEmailBundle\Response\VerifyEmailResponse;
class CustomVerifyEmailResponse extends VerifyEmailResponse
{
public function toArray(): array
{
return [
'status' => 'verification_sent',
'data' => $this->getData(),
];
}
}
Local Testing Use a local SMTP server (e.g., MailHog) and disable background processing:
verify_email:
send_email_in_background: false
Multi-Tenant Support
Override token_generator to include tenant ID:
public function generateToken(User $user): string
{
return hash('sha256', $user->email . $user->tenantId);
}
Analytics
Track verification clicks by extending the VerifyEmailController:
public function verify(Request $request, TokenGeneratorInterface $tokenGenerator)
{
// Your analytics logic here
return parent::verify($request, $tokenGenerator);
}
How can I help you explore Laravel packages today?