web-auth/webauthn-symfony-bundle
Symfony bundle integrating WebAuthn (passkeys/FIDO2) for strong, passwordless authentication. Provides registration and login flows, configuration, and helpers to add secure WebAuthn support to Symfony apps with minimal setup.
Installation
composer require web-auth/webauthn-symfony-bundle
Add the bundle to config/bundles.php (Symfony) or config/app.php (Laravel via Symfony bridge):
WebAuthn\Bundle\WebauthnBundle::class => ['all' => true],
Configuration
Copy and configure .env variables (or config/webauthn.php):
WEB_AUTHN_RP_ID=https://yourdomain.com
WEB_AUTHN_RP_NAME="Your App Name"
WEB_AUTHN_RP_ORIGIN=https://yourdomain.com
WEB_AUTHN_ATTESTATION=direct
WEB_AUTHN_USER_VERIFICATION=preferred
First Use Case: Registration
use WebAuthn\Bundle\WebauthnBundle\Service\WebAuthnService;
// In your controller:
$webauthn = $this->get('webauthn');
$user = $this->getUser(); // Your authenticated user
// Generate registration options
$options = $webauthn->getRegistrationOptions($user);
return new JsonResponse($options);
// Handle verification
$result = $webauthn->verifyRegistrationResponse($user, $data);
Generate Options
$options = $webauthn->getRegistrationOptions($user);
PublicKeyCredentialCreationOptions (JSON-serializable).<input type="hidden" id="challenge" value="...">).Frontend Integration Use the WebAuthn API to collect credentials:
navigator.credentials.create({ publicKey: options });
Verify Response
$result = $webauthn->verifyRegistrationResponse($user, $data);
if ($result->isSuccess()) {
$user->webauthnCredentials()->create([
'id' => $result->getCredentialId(),
'public_key' => $result->getPublicKey(),
'sign_count' => $result->getSignCount(),
'transports' => $result->getTransports(),
]);
}
Generate Assertion Options
$options = $webauthn->getAuthenticationOptions($user);
Frontend Assertion
navigator.credentials.get({ publicKey: options });
Verify Assertion
$result = $webauthn->verifyAuthenticationResponse($user, $data);
if ($result->isSuccess()) {
// Update sign count
$credential = $user->webauthnCredentials->find($result->getCredentialId());
$credential->update(['sign_count' => $result->getSignCount()]);
}
webauthn_credentials table (see schema).webauthn.login_required or custom middleware.Auth::attempt() if WebAuthn fails).Symfony\Component\HttpFoundation\Request via $request->getContent() for raw POST data.RP ID Mismatch
WEB_AUTHN_RP_ID matches your domain exactly (no subdomains unless configured).dd($webauthn->getRelyingParty()->getId());.User Verification
WEB_AUTHN_USER_VERIFICATION=required forces biometric/pin auth. Test with preferred first.Attestation
direct (default) requires HTTPS. Use none for testing (insecure).Credential Storage
credential_id as binary (LONGRAW in MySQL) or hex string. Avoid base64 URL-safe encoding in DB.Sign Count
sign_count on successful authentication to prevent replay attacks.# config/packages/webauthn.yaml
webauthn:
debug: true
$data (from frontend) to verify structure:
dd(json_decode($request->getContent(), true));
Expected keys: id, rawId, type, response (with authenticatorData, clientDataJSON, signature, etc.).Custom Credential Storage
Override WebAuthn\Bundle\WebAuthnBundle\Repository\CredentialRepositoryInterface for non-DB storage (e.g., Redis).
Event Listeners
Subscribe to webauthn.registration.success or webauthn.authentication.success:
// config/packages/webauthn.yaml
webauthn:
listeners:
registration_success: App\EventListener\WebAuthnRegistrationListener
Frontend Adaptors
Use libraries like @simplewebauthn/browser for React/Vue integration.
Multi-Factor
Combine with Laravel’s auth:attempt for password + WebAuthn:
if ($webauthn->verifyAuthenticationResponse($user, $data)->isSuccess()) {
Auth::login($user, true);
}
webauthn-test or mock responses in PHPUnit.getRegistrationOptions() if user/device hasn’t changed.rp_id to prevent open redirects (e.g., filter_var($rpId, FILTER_VALIDATE_URL)).How can I help you explore Laravel packages today?