Installation
The Symfony UX Initiative enables high-interaction applications directly from Twig templates without writing custom JavaScript. The WebAuthn Stimulus Controller makes implementing passwordless authentication as simple as adding a few Twig functions to your forms.
Prerequisites
Before installing the Stimulus Controller, you need:
Symfony UX configured - Follow the official Symfony UX documentation
WebAuthn Bundle installed - See Symfony Bundle Installation
Installation
Install the WebAuthn Stimulus Controller via Composer:
composer require web-auth/webauthn-stimulusThis command will automatically:
Install the PHP package
Register the Stimulus controller via Symfony Flex
Configure AssetMapper to import the necessary JavaScript files
Verify Installation
Check that the Stimulus controller is properly registered in your assets/controllers.json:
{
"controllers": {
"@web-auth/webauthn-stimulus": {
"enabled": true
}
}
}What's Included
The package provides a Stimulus controller that handles:
Registration flows - Calls
navigator.credentials.create()for youAuthentication flows - Calls
navigator.credentials.get()for youBase64 encoding/decoding - Automatically handles data conversion
Error handling - Gracefully handles common WebAuthn errors
Browser autofill - Supports conditional UI for passkey selection
Basic Usage
Once installed, you can use the Stimulus controller in your Twig templates with two simple functions:
stimulus_controller()- Attach the controller to your formstimulus_action()- Trigger WebAuthn operations on button clicks
Registration Form Example
<form
action="{{ path('app_register') }}"
method="post"
{{ stimulus_controller('@web-auth/webauthn-stimulus', {
creationOptionsUrl: path('webauthn.controller.creation.creation.new_user'),
creationResultField: 'input[name="attestation"]'
}) }}
>
<input type="text" name="username" required>
<input type="hidden" name="attestation">
<button
type="submit"
{{ stimulus_action('@web-auth/webauthn-stimulus', 'register') }}
>
Register
</button>
</form>Authentication Form Example
<form
action="{{ path('app_login') }}"
method="post"
{{ stimulus_controller('@web-auth/webauthn-stimulus', {
requestOptionsUrl: path('webauthn.controller.request.request.login'),
requestResultField: 'input[name="assertion"]'
}) }}
>
<input type="text" name="username" autocomplete="username webauthn">
<input type="hidden" name="assertion">
<button
type="submit"
{{ stimulus_action('@web-auth/webauthn-stimulus', 'signin') }}
>
Sign In
</button>
</form>Controller Options
The Stimulus controller accepts various configuration options:
Registration Options
creationOptionsUrl
string
Yes
URL endpoint that returns PublicKeyCredentialCreationOptions
creationResultField
string
Yes
CSS selector for hidden field to store attestation response
Authentication Options
requestOptionsUrl
string
Yes
URL endpoint that returns PublicKeyCredentialRequestOptions
requestResultField
string
Yes
CSS selector for hidden field to store assertion response
useBrowserAutofill
boolean
No
Enable conditional UI for browser autofill (default: false)
Testing Your Installation
Create a simple test page to verify everything works:
{% if app.user %}
<p>Logged in as: {{ app.user.userIdentifier }}</p>
<a href="{{ path('app_logout') }}">Logout</a>
{% else %}
<h2>Register</h2>
<form
action="{{ path('app_register') }}"
method="post"
{{ stimulus_controller('@web-auth/webauthn-stimulus', {
creationOptionsUrl: path('webauthn.controller.creation.creation.new_user'),
creationResultField: 'input[name="attestation"]'
}) }}
>
<label>
Username:
<input type="text" name="username" required>
</label>
<input type="hidden" name="attestation">
<button
type="submit"
{{ stimulus_action('@web-auth/webauthn-stimulus', 'register') }}
>
Register with WebAuthn
</button>
</form>
<h2>Login</h2>
<form
action="{{ path('app_login') }}"
method="post"
{{ stimulus_controller('@web-auth/webauthn-stimulus', {
requestOptionsUrl: path('webauthn.controller.request.request.login'),
requestResultField: 'input[name="assertion"]'
}) }}
>
<label>
Username:
<input type="text" name="username" autocomplete="username webauthn">
</label>
<input type="hidden" name="assertion">
<button
type="submit"
{{ stimulus_action('@web-auth/webauthn-stimulus', 'signin') }}
>
Sign In with WebAuthn
</button>
</form>
{% endif %}Last updated
Was this helpful?