Skip to content

User registration

The Symfony UX WebAuthn package provides Stimulus controllers to simplify the registration of new authenticators. This page explains how to register a new user with WebAuthn or add authenticators to an existing user account.

New user registration

When registering a new user, you need to collect basic information (username, display name) and then register their first authenticator.

Registration form

Your registration form needs: 1. A username field for the user's identifier 2. A display name field (optional, can default to username) 3. A hidden field for the attestation response

templates/registration/register.html.twig
<form
    action="{{ path('app_register') }}"
    method="post"
    {{ stimulus_controller('@web-auth/webauthn-stimulus',
        {
            creationOptionsUrl: path('webauthn.controller.creation.request.new_user'),
            creationResultField: 'input[name="attestation"]'
        }
    ) }}
>
    <label for="username">Username</label>
    <input
        type="text"
        id="username"
        name="username"
        required
        autocomplete="username webauthn"
    >

    <label for="displayName">Display Name</label>
    <input
        type="text"
        id="displayName"
        name="displayName"
        placeholder="John Doe"
    >

    <input type="hidden" id="attestation" name="attestation">

    <button
        type="submit"
        {{ stimulus_action('@web-auth/webauthn-stimulus', 'signup') }}
    >
        Register with Passkey
    </button>
</form>

Bundle configuration

Configure the bundle to handle new user registration:

config/packages/webauthn.yaml
webauthn:
    credential_repository: 'App\Repository\WebauthnCredentialRepository'
    user_repository: 'App\Repository\UserRepository'

    controllers:
        enabled: true
        creation:
            new_user:
                options_path: '/register/webauthn/options'
                result_path: '/register/webauthn'
                user_entity_guesser: 'App\Guesser\NewUserEntityGuesser'

User entity guesser

Create a guesser to handle new user registration:

<?php

declare(strict_types=1);

namespace App\Guesser;

use Symfony\Component\HttpFoundation\Request;
use Webauthn\Bundle\Security\Guesser\UserEntityGuesser;
use Webauthn\PublicKeyCredentialUserEntity;

final class NewUserEntityGuesser implements UserEntityGuesser
{
    public function findUserEntity(Request $request): PublicKeyCredentialUserEntity
    {
        $username = $request->request->get('username');
        $displayName = $request->request->get('displayName', $username);

        // Generate a unique user ID (64 bytes recommended)
        $userHandle = random_bytes(64);

        return PublicKeyCredentialUserEntity::create(
            $username,
            $userHandle,
            $displayName
        );
    }
}

Info

After successful registration, save both the user entity and the credential source to your database. The controller automatically validates the attestation response.

Adding authenticators to existing users

Existing users can register additional authenticators (backup keys, different devices).

Add authenticator form

templates/security/add_authenticator.html.twig
<form
    action="{{ path('app_add_authenticator') }}"
    method="post"
    {{ stimulus_controller('@web-auth/webauthn-stimulus',
        {
            creationOptionsUrl: path('webauthn.controller.creation.request.add_device'),
            creationResultField: 'input[name="attestation"]'
        }
    ) }}
>
    <input type="hidden" id="attestation" name="attestation">

    <button
        type="submit"
        {{ stimulus_action('@web-auth/webauthn-stimulus', 'signup') }}
    >
        Add New Authenticator
    </button>
</form>

Configuration for existing users

config/packages/webauthn.yaml
webauthn:
    controllers:
        enabled: true
        creation:
            add_device:
                options_path: '/devices/add/options'
                result_path: '/devices/add'
                user_entity_guesser: 'Webauthn\Bundle\Security\Guesser\CurrentUserEntityGuesser'

Success

The CurrentUserEntityGuesser automatically retrieves the currently authenticated user from the Symfony security context. No custom guesser needed!

Stimulus controller options

The registration controller accepts the following options:

Option Type Required Description
creationOptionsUrl string Yes URL to fetch credential creation options
creationResultField string Yes CSS selector for the hidden attestation field
autoRegister boolean No Automatically trigger registration on page load (default: false)

Auto-register example

Automatically start the registration process when the page loads:

{{ stimulus_controller('@web-auth/webauthn-stimulus',
    {
        autoRegister: true,
        creationOptionsUrl: path('webauthn.controller.creation.request.new_user'),
        creationResultField: 'input[name="attestation"]'
    }
) }}

Warning

Use autoRegister carefully as it immediately prompts the user for biometric authentication or security key interaction.

Dedicated registration controller

Info

New in v5.3.0: A dedicated registration controller is available alongside the combined controller.

The dedicated registration-controller provides a focused controller for credential registration with enhanced features like conditional create support.

Basic usage

templates/registration/register.html.twig
<form
    action="{{ path('app_register') }}"
    method="post"
    {{ stimulus_controller('@web-auth/webauthn-stimulus/registration', {
        optionsUrl: path('webauthn.controller.creation.request.new_user'),
        resultUrl: path('app_register'),
        submitViaForm: true
    }) }}
>
    <input
        type="text"
        name="username"
        required
        {{ stimulus_target('@web-auth/webauthn-stimulus/registration', 'username') }}
    >
    <input
        type="hidden"
        name="attestation"
        {{ stimulus_target('@web-auth/webauthn-stimulus/registration', 'result') }}
    >

    <button
        type="button"
        {{ stimulus_action('@web-auth/webauthn-stimulus/registration', 'register') }}
    >
        Register with Passkey
    </button>
</form>

Registration controller options

Option Type Default Description
optionsUrl string /registration/options URL to fetch creation options
resultUrl string /registration/verify URL to submit the attestation response
submitViaForm boolean false Submit via form instead of fetch
successRedirectUri string - URL to redirect after successful registration
autoRegister boolean false Auto-start registration on page load

Registration controller targets

Target Description
username Input field for the username
attestation Select field for attestation preference
residentKey Select field for resident key preference
userVerification Select field for user verification preference
authenticatorAttachment Select field for authenticator attachment preference
result Hidden input for the attestation response

Error handling

Handle registration errors in your custom event listeners:

document.addEventListener('webauthn:registration:error', (event) => {
    console.error('Registration failed:', event.detail);
    alert('Could not register authenticator: ' + event.detail.message);
});

document.addEventListener('webauthn:registration:success', (event) => {
    console.log('Registration successful!');
});

See also