To authenticate your users, you need to create a PublicKeyCredentialRequestOptions
object. You can create this object using the .... Similarly to the authentication registration process, there is another approach.
The bundle provides a factory and manages profiles to ease the creation of the options. The factory is available as a public service: Webauthn\Bundle\Service\PublicKeyCredentialRequestOptionsFactory
. To use it, you must first create a least one profile in your configuration file.
No other option is needed to create a profile!
With this profile, now we can create options with the following code lines:
As mentioned earlier, it is preferable to indicate the Relying Party ID. By default it is set to null
i.e. the current domain is used.
By default, the length of the challenge is 32 bytes. You may need to select a smaller or higher length. This length can be configured for each profile:
The default timeout is set to 60 seconds (60 000 milliseconds). You can change this value as follow:
By default, the authenticator will verify the user if it is possible. You can enforce or disable the user verification using this option.
The mechanism for generating public key credentials, as well as requesting and generating Authentication assertions, can be extended to suit particular use cases. Each case is addressed by defining a registration extension.
The example below is tatolly fictive. Some extensions are defined in the specification but the supports depends on the authenticators and on the relying parties.
Symfony is a very popular framework and an official bundle is provided in the package web-auth/webauthn-symfony-bundle
.
If you use Laravel, you may be intersted in this project: https://github.com/asbiin/laravel-webauthn
If you are using Symfony Flex then the bundle will automatically be installed. Otherwise you need to add it in your AppKernel.php
file:
At the moment, only Doctrine is supported, however there is no technical constraint to allow other data storage systems.
The minimal configuration requires the user repository and the pk credential source repository.
Now you may want to:
As described in the previous pages, you need to create a PublicKeyCredentialCreationOptions
object to register new authenticators. You can create this object using the .... But there is another way to do that.
The bundle provides a factory and manages profiles to ease the creation of the options. The factory is available as a public service: Webauthn\Bundle\Service\PublicKeyCredentialCreationOptionsFactory
. To use it, you must first create a least one profile in your configuration file.
The name
is mandatory ; other options are null
by default.
The option id is highly recommended. See this page for acceptable values.
With this profile, now we can create options with the following code lines:
By default, the length of the challenge is 32 bytes. You may need to select a smaller or higher length. This length can be configured for each profile:
The default timeout is set to 60 seconds (60 000 milliseconds). You can change this value as follow:
This set of options allows you to select authenticators depending on their capabilities. The values are described in the advanced concepts of the protocol.
This option indicates the algorithms allowed for your application. By default, a large list of algorithms is defined, but you can add custom algorithms or reduce the list.
The order is important. Preferred algorithms go first.
It is not recommended to change the default list unless you exactly know what you are doing.
If you need the attestation of the authenticator, you can specify the preference regarding attestation conveyance during credential generation.
Please note that the metadata service is mandatory to use this option.
The use of Attestation Statements is generally not recommended unless you REALLY need this information.
The mechanism for generating public key credentials, as well as requesting and generating Authentication assertions, can be extended to suit particular use cases. Each case is addressed by defining a registration extension.
The example below is tatolly fictive. Some extensions are defined in the specification but the supports depends on the authenticators and on the relying parties.
To authenticate your users, you can follow the steps described on the previous page. But as Symfony offers a firewall, you may prefer to use it instead of writing a new authentication process and get all advantages of this firewall.
First of all, you must install the dedicated bundle: symfony/security-bundle
.
Next, you must create a request profile as showed here.
Then, you must have a PSR-7 message factory service. We recommend the use of nyholm/psr7
, but feel free to use any other compatible library.
The PSR-7 Message Factory shall be available as a service.
That's it! You can now protect any route as usual.
Prior to the authentication of the user, you must get a PublicKey Credential Request Options object. To do so, send a POST request to /login/options
.
The body of this request is a JSON object that must contain a username
member with the name of the user being authenticated.
It is mandatory to set the Content-Type header to application/json
.
In case of success, you receive a valid PublicKeyCredentialRequestOptions
object and your user will be asked to interact with its security devices.
The default path is /login/options
. You can change it if needed:
When the user touched the security device, you will receive a response from it. You just have to send a POST request to /login
.
The body of this request is the response of the security device.
It is mandatory to set the Content-Type header to application/json
.
The default path is /assertion/result
. You can change that path is needed:
Your user can now be authenticated and retrieved as usual.
You can customize the responses returned by the firewall by using a custom handler. This could be useful when using an access token manager (e.g. LexikJWTAuthenticationBundle) or to modify the responses.
There are 3 types of responses and handlers:
Request options,
Authentication Success,
Authentication Failure,
This handler is called when a client sends a valid POST request to the options_path
. The default Request Options Handler is Webauthn\Bundle\Security\Handler\DefaultRequestOptionsHandler
. It returns a JSON Response with the Public Key Credential Request Options objects in its body.
Your custom handler have to implement the interface Webauthn\Bundle\Security\Handler\RequestOptionsHandler
and be declared as a service.
When done, you can set your new service in the firewall configuration:
This handler is called when a client sends a valid assertion from the authenticator. The default handler is Webauthn\Bundle\Security\Handler\DefaultSuccessHandler
.
Your custom handler have to implement the interface Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface
and be declared as a container service.
When done, you can set your new service in the firewall configuration:
This handler is called when an error occurred during the authentication process. The default handler is Webauthn\Bundle\Security\Handler\DefaultFailureHandler
.
Your custom handler have to implement the interface Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface
and be declared as a container service.
When done, you can set your new service in the firewall configuration:
Webauthn authentication is a 2 steps round trip authentication:
Request options issuance
Authenticator assertion verification
It is needed to store the request options and the user entity associated to it to verify the authenticator assertions.
By default, the firewall uses Webauthn\Bundle\Security\Storage\SessionStorage
. This storage system stores the data in a session.
If this behaviour does not fit on your needs (e.g. you want to use a database, REDIS…), you can implement a custom data storage for that purpose. Your custom storage system have to implement Webauthn\Bundle\Security\Storage\RequestOptionsStorage
and declared as a container service.
When done, you can set your new service in the firewall configuration:
The security token returned by the firewall sets some attributes depending on the assertion and the capabilities of the authenticator. The attributes are:
IS_USER_PRESENT
: the user was present during the authentication ceremony. This attribute is usually set to true
by Webauthn authenticators,
IS_USER_VERIFIED
: the user was verified by the authenticator. Verification may be performed by several means including biometrics ones (fingerprint, iris, facial recognition…).
You can then set constraints to the access controls.
With Doctrine, you have to indicate how to store the Credential Source objects. Hereafter an example an entity. In this example we add an entity id
and a custom field created_at
. We also indicate the repository as we will have a custom one.
As the ID must have a fixed length and because the credentialId
field of Webauthn\PublicKeyCredentialSource
haven’t such requirement and is a binary string, we need to declare our own id
field.
To ease the integration into your application, the bundle provides a concrete class that you can extend.
In this following example, we extend that class and add a method to get all credentials for a specific user handle. Feel free to add your own methods.
We must override the method saveCredentialSource because we may receive Webauthn\PublicKeyCredentialSource
objects instead of App\Entity\PublicKeyCredentialSource
.
This repository should be declared as a Symfony service.
With Symfony 4, this is usually done automatically
In a Symfony application context, you usually have to manage several user entities. Thus, in the following example, the user entity class will extend the required calls and implements the interface provided by the Symfony Security component.
Feel free to add the necessary setters as well as other fields you need (creation date, last update at…).
Please note that the ID of the user IS NOT generated by Doctrine and must be a string. We highly recommend you to use UUIDs.
The following example uses Doctrine to create, persist or perform queries using the User objects created above.
This repository should be declared as a Symfony service.
With Symfony 4, this is usually done automatically