elao/voucher-authentication-bundle
Symfony bundle enabling voucher (email link) authentication. Register the bundle, import routes, and enable the “voucher” firewall. Generate and persist disposable voucher tokens in your app or via CLI, then send users a login/activation link with an optional TTL.
Installation
composer require elao/voucher-authentication-bundle
Add to config/bundles.php (Symfony) or config/app.php (Laravel via bridge):
Elao\VoucherAuthenticationBundle\ElaoVoucherAuthenticationBundle::class => ['all' => true],
Configuration Publish the default config:
php artisan vendor:publish --provider="Elao\VoucherAuthenticationBundle\ElaoVoucherAuthenticationBundle" --tag="config"
Key settings to review:
voucher_expiration (default: 3600 seconds)voucher_length (default: 16 chars)email_template (path to Twig template for voucher emails)First Use Case: Email Link Authentication
use Elao\VoucherAuthenticationBundle\Generator\VoucherGeneratorInterface;
$generator = $this->container->get(VoucherGeneratorInterface::class);
$voucher = $generator->generate($user);
/login?voucher=ABCD1234EFGH5678
User Registration/Request Flow
use Elao\VoucherAuthenticationBundle\Event\VoucherGeneratedEvent;
public function onVoucherGenerated(VoucherGeneratedEvent $event)
{
$voucher = $event->getVoucher();
$user = $event->getUser();
Mail::to($user->email)->send(new VoucherEmail($voucher));
}
Login Handling
voucher guard:
// config/auth.php
'guards' => [
'voucher' => [
'driver' => 'session',
'provider' => 'users',
],
],
VoucherAuthenticator to handle voucher validation:
public function authenticate(Request $request)
{
$voucher = $request->query->get('voucher');
$user = $this->voucherManager->findUserByVoucher($voucher);
if ($user && !$this->voucherManager->isVoucherExpired($voucher)) {
$this->voucherManager->consumeVoucher($voucher);
return $this->auth->login($user);
}
return null;
}
Integration with Forms
<form method="POST" action="/login">
@csrf
<input type="hidden" name="voucher" value="{{ request('voucher') }}">
<button type="submit">Login with Voucher</button>
</form>
VoucherManager to track failed attempts per voucher/IP.consumeVoucher() to allow reuse (e.g., for shared accounts).expires_at, used_at) in the database.Voucher Expiration
voucher_expiration in config if needed.if ($this->voucherManager->isVoucherExpired($voucher)) {
throw new VoucherExpiredException('Voucher has expired.');
}
Database Schema
vouchers table with columns:
id, user_id, voucher, expires_at, used_at.php artisan vendor:publish --provider="Elao\VoucherAuthenticationBundle\ElaoVoucherAuthenticationBundle" --tag="migrations"
Security Risks
try-catch for voucher validation:
try {
$user = $this->voucherManager->findUserByVoucher($voucher);
} catch (VoucherNotFoundException $e) {
// Log generically: "Invalid voucher provided."
}
Testing
VoucherGeneratorInterface to avoid real voucher creation in tests:
$generator = $this->createMock(VoucherGeneratorInterface::class);
$generator->method('generate')->willReturn('TEST1234TEST1234');
Custom Voucher Generation
VoucherGenerator to enforce stricter rules (e.g., alphanumeric-only):
class CustomVoucherGenerator extends VoucherGenerator
{
protected function generateRandomString()
{
return substr(strtoupper(bin2hex(random_bytes(8))), 0, 16);
}
}
# config/services.yaml
Elao\VoucherAuthenticationBundle\Generator\VoucherGeneratorInterface: '@custom_voucher_generator'
Email Templates
resources/views/elao_voucher_authentication/email.txt.twig):
Hello {{ user.email }},
Your voucher is: {{ voucher.voucher }}
It expires at: {{ voucher.expiresAt|date('Y-m-d H:i') }}
email_template: 'path/to/custom_template'
Debugging
DB::enableQueryLog();
$user = $this->voucherManager->findUserByVoucher($voucher);
dd(DB::getQueryLog());
SELECT * FROM vouchers WHERE expires_at < NOW();
Performance
vouchers(voucher) and vouchers(user_id) for large-scale use:
CREATE INDEX voucher_index ON vouchers(voucher);
CREATE INDEX user_voucher_index ON vouchers(user_id);
How can I help you explore Laravel packages today?