Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Adoption by web browsers
Webauthn is now supported by all main web browsers:
Mozilla Firefox 60+ and Firefox for Android 68+
Google Chrome 67+
Microsoft EDGE 18+ and Microsoft EDGE Chromium 79+
Opera 54+
Safari 13+ and iOS Safari 13.3+
Android Browser 76+
For more information and limitation on these browsers, please have a look at the following page: https://caniuse.com/#feat=webauthn
Disclaimer: you should not ask for the Attestation Statement unless you are working on an application that requires a high level of trust (e.g. Banking/Financial Company, Government Agency...).
During the Attestation Ceremony (i.e. the registration of the authenticator), you can ask for the Attestation Statement of the authenticator. The Attestation Statements have one of the following types:
None (none
): no Attestation Statement is provided
Basic Attestation (basic
): Authenticator’s attestation key pair is specific to an authenticator model.
Surrogate Basic Attestation (or Self Attestation - self
): Authenticators that have no specific attestation key use the credential private key to create the attestation signature
Attestation CA (AttCA
): Authenticators are based on a Trusted Platform Module (TPM). They can generate multiple attestation identity key pairs (AIK) and requests an Attestation CA to issue an AIK certificate for each.
Anonymization CA (AnonCA
): Authenticators use an Anonymization CA, which dynamically generates per-credential attestation certificates such that the attestation statements presented to Relying Parties do not provide uniquely identifiable information.
Elliptic Curve based Direct Anonymous Attestation (ECDAA
): Authenticator receives direct anonymous attestation (DAA) credentials from a single DAA-Issuer. These DAA credentials are used along with blinding to sign the attested credential data.
The Metadata Statements are issued by the manufacturers of the authenticators. These statements contain details about the authenticators (supported algorithms, biometric capabilities...) and all the necessary information to verify the Attestation Statements generated during the attestation ceremony.
There are several possible sources to get these Metadata Statements. The main source is the FIDO Alliance Metadata Service that allows fetching statements on-demand, but some of them may be provided by other means.
The FIDO Alliance Metadata Service provides a limited number of Metadata Statements. It is mandatory to get the statement from the manufacturer of your authenticators otherwise the Attestation Statement won't be verified and the Attestation Ceremony will fail.
What is an authenticator?
An Authenticator is a cryptographic entity used to generate a public key credential and registered by a Relying Party (i.e. an application). This public key is used to authenticate by potentially verifying a user in the form of an authentication assertion and other data.
Authenticators may have additional features such as PIN code or biometric sensors (fingerprint, facial recognition…) that offer user verification.
The roaming authenticator may have different forms. The most common form is a USB device the user plugs into its computer. It can be a paired Bluetooth device or a card with NFC capabilities.
Authenticators of this class are removable from, and can "roam" among, client devices.
A platform authenticator is usually not removable from the client device. For example an Android smartphone or a Windows 10 computer with the associated security chips can act as an authenticator.
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.
Standard extensions are usually listed in the dedicated IANA Registry available at
Among the available extensions, you have:
loc
: The location registration extension and authentication extension provides the client device's current location to the WebAuthn Relying Party, if supported by the client platform and subject to user consent.
hmac-secret
: This registration extension and authentication extension enables the platform to retrieve a symmetric secret scoped to the credential from the authenticator.
minPinLength
: This registration extension returns the current minimum PIN length value to the Relying Party.
This library is ready to handle extension inputs and outputs, but no concrete implementations are provided.
It is up to you, depending on the extensions you want to support, to create the extension handlers.
How to install the library or the Symfony bundle?
This framework contains several sub-packages that you don’t necessarily need. It is highly recommended to install what you need and not the whole framework.
The preferred way to install the library you need is to use composer:
Hereafter the dependency tree:
web-auth/webauthn-lib
: this is the core library. This package can be used in any PHP project or within any popular framework (Laravel, CakePHP…)
web-auth/webauthn-symfony-bundle
: this is a Symfony bundle that ease the integration of this authentication mechanism in your Symfony project.
The core library also depends on web-auth/cose-lib
and web-auth/metadata-service
. What are these dependencies?
web-auth/cose-lib
contains several cipher algorithms and COSE key support to verify the digital signatures sent by the authenticators during the creation and authentication ceremonies. These algorithms are compliant with the RFC8152. This library can be used by any other PHP projects. At the moment only signature algorithms are available, but it is planned to add encryption algorithms.
web-auth/metadata-service
provides classes to support the Fido Alliance Metadata Service. If you plan to use Attestation Statements during the creation ceremony, this service is mandatory. Please note that Attestation Statements decreases the user privacy as they may leak data that allow to identify a specific user. The use of Attestation Statements and this service are generally not recommended unless you REALLY need this information. This library can also be used by any other PHP projects.
The total size of the core package is approximately 760ko. Hereafter the detail for each component:
web-auth/cose-lib
: 85ko
web-auth/metadata-service
: 81ko
web-auth/webauthn-lib
: 207ko
web-auth/webauthn-symfony-bundle
: 385ko
The total size of the core package + the direct dependencies is approximately 1.7Mo.
aka the application you are interacting with
The Relying Party (or rp
) corresponds to the application that will ask for the user to interact with the authenticator.
The library provides a simple class to handle the rp information: Webauthn\PublicKeyCredentialRpEntity
.
This $rpEntity
object will be useful for the next steps.
In the example above, we created a simple relying party object with it’s name. The relying party may also have an ID that corresponds to the domain applicable for that rp
. By default, the relying party ID is null
i.e. the current domain will be used.
It may be useful to specify the rp
ID, especially if your application has several sub-domains. The rp ID can be set during the creation of the object as 2nd constructor parameter.
Even if it is optional, we highly recommend setting the application ID
The rp
ID shall be the domain of the application without the scheme, userinfo, port, path, user…. IP addresses are not allowed either.
Allowed: www.sub.domain.com
, sub.domain.com
, domain.com
Not allowed:
www.sub.domain.com:1337
, https://domain.com:443
, sub.domain.com/index
, https://user:password@www.domain.com
.
12.65.76.43 or [2001:db8:85a3:8d3:1319:8a2e:370:7348]
The domain localhost
can be used if the browser considers the context is safe (especially the IP address corresponds to a local address)
The Relying Party ID should be determined depending on the common URLs for your web application.
If you have a web application that can be reached at https://m.my-app.com (for mobiles) and https://my-app.com or https://www.my-app.com (for other devices), your Relying Party ID should be my-app.com
.
If the domain is shared between sub-projects, the rp ID should be limited to that sub-projects.
For example, a web site is located at https://(www.)site1.host.com
and another at https://(www.)site2.host.com
, then the Relying Party IDs should be site1.host.com
and site2.host.com
respectively. If you set host.com
, there is a risk that users from site1.host.com
can log in at site2.host.com
.
Your application may also have a logo. You can indicate this logo as third argument. Please note that for safety reason this icon is a priori authenticated URL i.e. an image that uses the data
scheme.
The Webauthn specification does not set any limit for the length of the third argument.
The icon may be ignored by browsers, especially if its length is greater than 128 bytes.
It's all about users
A User Entity object represents a user in the Webauthn context. It has the following constraints:
The user ID must be unique and must be a string,
The username must be unique,
Hereafter a minimalist example of user entity:
The username can be composed of any displayable characters, including emojis. Username "😝🥰😔" is perfectly valid.
Developers should not add rules that prevent users from choosing the username they want.
For privacy reasons, it is not recommended using the e-mail as username.
As for the rp
Entity, the User Entity may have an icon. This icon must also be secured.
The Webauthn specification does not set any limit for the length of the icon.
The icon may be ignored by browsers, especially if its length is greater than 128 bytes.
The User Entity Repository manages all Webauthn users of your application.
There is no interface to implement or abstract class to extend so that it should be easy to integrate it in your application. You may already have a user repository.
Whatever database you use (MySQL, pgSQL…), it is not necessary to create relationships between your users and the Credential Sources.
It shall be noted that the Symfony bundle will need a user entity repository. This service shall implement Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepository
.
The methods required by the interface are as follow:
public function findOneByUsername(string $username): ?PublicKeyCredentialUserEntity;
This method tries to find out a user entity from the username.
public function findOneByUserHandle(string $userHandle): ?PublicKeyCredentialUserEntity;
This method tries to find out a user entity from the user handle i.e. the user ID.
public function generateNextUserEntityId(): string;
This method creates a user entity ID. Note that this method SHALL NOT save that ID. Its main purpose generate a unique ID that could be used for a user entity object at a later stage.
public function saveUserEntity(PublicKeyCredentialUserEntity $userEntity): void;
This method saves the user entity. If the user entity already exists, it should throw an exception.
Authenticator details and how to manage them
After the registration of an authenticator, you will get a Public Key Credential Source object. It contains all the credential data needed to perform user authentication and much more.
Each Credential Source is managed using the Public Key Credential Source Repository.
The library does not provide any concrete implementation. It is up to you to create it depending on your application constraints. This only constraint is that your repository class must implement the interface Webauthn\PublicKeyCredentialSourceRepository
.
The PublicKeyCredentialSourceRepository
interface requires the following methods to be implemented:
public function findOneByCredentialId(string $publicKeyCredentialId): ?PublicKeyCredentialSource;
: thie method retreive a key source object from the credential ID.
public function findAllForUserEntity(PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity): array;
: this method lists all key sources associated to the user entity
public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource): void;
: this method saves the key source in your storage (files, database...)
Browsers may support the Token Binding protocol (see ). This protocol defines a way to bind a token (the Responses in the Webauthn context) to the underlying TLS layer.
When receiving a Webauthn Response, the property tokenBinding
in the Webauthn\CollectedClientData
object has one of the following values:
null
: the token binding is not supported by the browser
"supported"
: the browser supports token binding, but no negotiation was performed during the communication
"present"
: the browser supports token binding, and it is present in the response. The token binding ID is provided.
This feature is not yet implemented in the library, but you can decide how the library will react in case of the presence of the token binding ID.
The library provides two concrete classes for the moment:
Webauthn\TokenBinding\IgnoreTokenBindingHandler
: the library will ignore the token binding (recommended),
Webauthn\TokenBinding\TokenBindingNotSupportedHandler
: the library will throw an exception if the token binding is present.
You can change this behavior by creating your own implementation. The handler must implement the interface Webauthn\TokenBinding\TokenBindingHandler
.
Registration and Authentication process overview
In the Webauthn context, there are two ceremonies:
The attestation ceremony: it corresponds to the registration of a new authenticator,
The assertion ceremony: it is used for the authentication of a user.
For both ceremonies, there are two steps to perform:
The creation of options: these options are sent to the authenticator and indicate what to do and how.
The response of the authenticator: after the user interacted with the authenticator, the authenticator computes a response that has to be verified.
Depending on the options and the capabilities of the authenticator, the user interaction may differ. It can be a simple touch on a button or a complete authentication using biometric means (PIN code, fingerprint, facial recognition…).
Overview of the framework
Webauthn defines an API enabling the creation and use of strong, attested, scoped, public key-based credentials by web applications, for the purpose of strongly authenticating users.
The complete specification can be found on .
This framework contains PHP libraries and Symfony bundle to allow developers to integrate that authentication mechanism into their web applications.
Naming things may be complicated. That’s why the following rule applies on the whole framework: the name of classes, constants and properties are identical to the ones you will find in the specification.
As an example, the shows an object named AuthenticatorAssertionResponse
that extends AuthenticatorResponse
with the following properties:
authenticatorData
signature
userHandle
You will find in the PHP class provided by the library.
Attestation Types
Empty
Basic
Self
Private CA
Anonymization CA
Elliptic Curve Direct Anonymous Attestation (ECDAA)
Attestation Formats
FIDO U2F
Packed
TPM
Android Key
Android Safetynet
Apple
Token Binding support
Cose Algorithms
RS1, RS256, RS384, RS512
PS256, PS384, PS512
ES256, ES256K, ES384, ES512
ED25519
Extensions
Supported (not fully tested)
appid extension (compatibility with FIDO U2F authenticator)
The framework is already compatible with all authenticators except the ones that use ECDAA Attestation format.
The ECDAA Attestation format is very rare at that time (April 2021) thus this framework can safely be used in production.
The compliance of the framework is ensured by running unit and functional tests during its development.
I bring solutions to your problems and answer your questions.
Requests for new features, bug fixed and all other ideas to make this framework useful are welcome.
You have just found a bug?
First of all, thank you for contributing.
Bugs or feature requests can be posted online on the GitHub issues section of the project.
Few rules to ease code reviews and merges:
You MUST follow the for coding standards.
You MUST run the test suite (see below).
You MUST write (or update) unit tests when bugs are fixed or features are added.
You SHOULD write documentation.
You MAY follow the and .
We use the following branching workflow:
Each minor version has a dedicated branch (e.g. v1.1, v1.2, v2.0, v2.1…)
The default branch is set to the last minor version (e.g. v2.1).
To contribute use , please, write commit messages that make sense, and rebase your branch before submitting your PR.
Your PR should NOT be submitted to the master branch but to the last minor version branch or to another minor version in case of bug fix.
install composer: curl -s http://getcomposer.org/installer | php
install dependencies: php composer.phar install
run tests: vendor/bin/phpunit
It is also tested using the official FIDO Alliance testing tools. The status of the compliance tests are . At the time of writing (end of April 2021), the main features and algorithms are supported and 99% of the tests pass.
If you really love that project, and the work I have done or if you want I prioritize your issues, then !
If you feel comfortable writing code, you could try to fix or .
Do not forget to follow .
If you think you have found a security issue, DO NOT open an issue. .
If you have troubles during the development of your application or if you want to keep track of every critical/error messages in production, you can use a PSR-3 compatible logger.
The following classes provide a setLogger
function to pass your PSR-3 logger instance.
Webauthn\AttestationStatement\AttestationObjectLoader
Webauthn\AuthenticatorAssertionResponseValidator
Webauthn\AuthenticatorAttestationResponseValidator
Webauthn\PublicKeyCredentialLoader
Webauthn\Counter\ThrowExceptionIfInvalid
During this step, your application will send a challenge to the list of registered devices of the user. The security token will resolve this challenge by adding information and digitally signing the data.
To perform a user authentication using a security device, you need to instantiate a Webauthn\PublicKeyCredentialRequestOptions
object.
Let’s say you want to authenticate the user we used earlier. This options object will need:
A challenge (random binary string)
The list with the allowed credentials (may be an option in certain circumstances)
Optionally, you can customize the following parameters:
A timeout
The Relying Party ID i.e. your application domain
The user verification requirement
Extensions
The PublicKeyCredentialRequestOptions
object is designed to be easily serialized into a JSON object. This will ease the integration into an HTML page or through an API endpoint.
The timeout default value is set to null
. If you want to set a value, pleaase read the following recommended behavior showed in the specification:
If the user verification is discouraged
, timeout should be between 30 and 180 seconds
If the user verification is preferred
or required
, the range is 300 to 600 seconds (5 to 10 minutes)
The user trying to authenticate must have registered at least one device. For this user, you have to get all Webauthn\PublicKeyCredentialDescriptor
associated to his account.
For usernameless authentication, please read the dedicated page. In this case no Public Key Credential Descriptors should be passed to the the options.
Eligible authenticators are filtered and only capable of satisfying this requirement will interact with the user. Please refer to the User Verification page for all possible values.
Please refer to the Extension page to know how to manage authentication extensions.
The way you receive this response is out of scope of this library. In the previous example, the data is part of the query string, but it can be done through a POST request body or a request header.
What you receive must be a JSON object that looks like as follows:
There are two steps to perform with this object:
Load the data
Verify the loaded data against the assertion options set above
This step is exactly the same as the one described in Public Key Credential Creation process.
Now we have a fully loaded Public Key Credential object, but we need now to make sure that:
The authenticator response is of type AuthenticatorAssertionResponse
This response is valid.
The first is easy to perform:
The second step is the verification against the Public Key Assertion Options we created earlier.
The Authenticator Assertion Response Validator service (variable $authenticatorAssertionResponseValidator
) will check everything for you.
If no exception is thrown, the response is valid and you can continue the authentication of the user.
The Public Key Credential Source returned allows you to know which device was used by the user.
To launch a Webauthn server, you will need the following components:
That’s a lot off classes! But don’t worry, as their configuration is the same for all your application, you just have to set them once. Let’s see all of these in the next sections.
The Public Key Credential Source Repository must implement Webauthn\PublicKeyCredentialSourceRepository
. It will retrieve the credential source and update them when needed.
You can implement the required methods the way you want: Doctrine ORM, file storage…
The token binding handler is a service that will verify if the token binding set in the device response corresponds to the one set in the request.
At the time of writing, we recommend to ignore this feature. Please refer to the dedicated page for more information.
Every Creation Responses contain an Attestation Statement. This attestation contains data regarding the authenticator depending on several factors such as its manufacturer and model, what you asked in the options, the capabilities of the browser or what the user allowed.
The user may refuse to send information about the security token for privacy reasons.Hereafter the types of attestations you may have:
The following attestation types are supported. Note that you should only use the none
one unless you have specific needs described in the dedicated page.
none
: no attestation is provided.
fido-u2f
: for non-FIDO2 compatible devices (old U2F security tokens).
packed
: generally used by authenticators with limited resources (e.g., secure elements). It uses a very compact but still extensible encoding method.
android key
: commonly used by old or disconnected Android devices.
android safety net
: for new Android devices like smartphones.
trusted platform module
: for devices with built-in security chips.
apple
: for Apple devices
This object will load the Attestation statements received from the devices. It will need the Attestation Statement Support Manager created above.
This object will load the Public Key using from the Attestation Object.
If you use extensions, you may need to check the value returned by the security devices. This behaviour is handled by an Extension Output Checker Manager.
You can add as many extension checkers as you want. Each extension checker must implement Webauthn\AuthenticationExtensions\ExtensionOutputChecker
and throw a Webauthn\AuthenticationExtensions\ExtensionOutputError
in case of an error.
More about that in this page.
The Webauthn data verification is based on cryptographic signatures and thus you need to provide cryptographic algorithms to perform those checks.
We recommend the use of the following algorithms to cover all types of Authenticators. Feel free to adapt this list depending on your needs.
This object is what you will directly use when receiving Attestation Responses (authenticator registration).
This object is what you will directly use when receiving Assertion Responses (user authentication).
Disclaimer: you should not ask for the Attestation Statement unless you are working on an application that requires a high level of trust (e.g. Banking/Financial Company, Government Agency...).
First of all, you must prepare an Attestation Metadata Repository. This service will manage all Metadata Statements depending on their sources (local storage or distant service).
Your Metadata Statement Repository must implement the interface Webauthn\MetadataService\MetadataStatementRepository
that has two methods:
findOneByAAGUID(string $aaguid)
: this method retrieves the MetadataStatement
object with AAGUID. It shall return null
in case of the absence of the MDS.
The library does not provide any Metadata Statement Repository. It is up to you to select the MDS suitable for your application and store them in your database.
There are few steps to acheive. First, you have to add support classes for all attestation statement types into your Attestation Metatdata Manager.
The Android SafetyNet Attestation Statement is a JWT that can be verified by the library, but can also be checked online by hitting the Google API. This method drastically increase the security for the attestation type but requires a PSR-18 compatible HTTP Client and an API key.
Next, you must inject the Metadata Statement Repository to your Attestation Object Loader.
By default, no Attestation Statement is asked to the Authenticators (type = none
). To change this behavior, you just have to set the corresponding parameter in the Webauthn\PublicKeyCredentialCreationOptions
object.
There are 3 conveyance modes available using PHP constants provided by the class Webauthn\PublicKeyCredentialCreationOptions
:
ATTESTATION_CONVEYANCE_PREFERENCE_NONE
: the Relying Party is not interested in authenticator attestation (default)
ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT
: the Relying Party prefers an attestation conveyance yielding verifiable attestation statements, but allows the client to decide how to obtain such attestation statements.
ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT
: the Relying Party wants to receive the attestation statement as generated by the authenticator.
Examples for dynamic interactions
You will interact with the authenticators through an HTML page and Javascript using the Webauthn API.
A package is available at https://github.com/web-auth/webauthn-helper. It contains functions that will ease the interaction with the login or the registration endpoints.
It is mandatory to use the HTTPS scheme to use Webauthn otherwise it will not work.
You can use npm or yarn to install the package:
Additional options can be set during the registration process. See the section “Deep into the framework” to know more. Hereafter another example:
The specification Webauthn L2 deprecates the use of the parameter requireResidentKey
; you should use residentKey instead with one of the following value: required
, preferred
or discouraged
.
To have the same behavior as above, please use required
.
As done during the registration, additional options are available. See the section “Deep into the framework” to know more. Hereafter another example:
With Webauthn, it is possible to authenticate a user without username. This behavior implies several constraints:
During the registration of the authenticator, a Resident Key must have been asked,
The user verification is required,
The list of allowed authenticators must be empty
In case of failure, you should continue with the standard authentication process i.e. by asking the username of the user.
Selection criteria for the registration of the authenticator:
The Request Options:
The default values for the user verification and the resident key are set to preferred and resident keys may be created if the authenticator is compatible. This means that some users may log in without username.
If your are working on a development environment, https
may not be available but the context could be considered as secured. You can bypass the scheme verification by passing the list of rpIds you consider secured.
Please be careful using this feature. It should NOT be used in production.
By default, any type of authenticator can be used by your users and interact with you application. In certain circumstances, you may need to select specific authenticators e.g. when user verification is required.
The Webauthn API and this library allow you to define a set of options to disallow the registration of authenticators that do not fulfill with the conditions.
The class Webauthn\AuthenticatorSelectionCriteria
is designed for this purpose. It is used when generating the Webauthn\PublicKeyCredentialCreationOptions
object.
You can indicate if the authenticator must be attached to the client (platform authenticator i.e. it is usually not removable from the client device) or must be detached (roaming authenticator).
Possible values are:
AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_NO_PREFERENCE
: there is no requirement (default value),
AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_PLATFORM
: the authenticator must be attached,
AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM
: must be a roaming authenticator.
A primary use case for platform authenticators is to register a particular client device as a "trusted device" for future authentication. This gives the user the convenience benefit of not needing a roaming authenticator, e.g., the user will not have to dig around in their pocket for their key fob or phone.
With this criterion, a Public Key Credential Source will be stored in the authenticator, client or client device. Such storage requires an authenticator capable to store such a resident credential.
A resident key shall be created you want to authenticate users without username.
With this example, with require the user verification (PIN, fingerprint...), a resident key and an authenticator embedded onto a device. This is typacally what you will require for Windows Hello or Face ID authentication.
During this step, your application will send a challenge to the device. The device will resolve this challenge by adding information and digitally signing the data.
The application will check the response from the device and get its credential ID. This ID will be used for further authentication requests.
To associate a device to a user, you need to instantiate a Webauthn\PublicKeyCredentialCreationOptions
object.
It will need:
The Relying Party
The User data
A challenge (random binary string)
A list of supported public key parameters i.e. an algorithm list (at least one)
Optionally, you can customize the following parameters:
A timeout
A list of public key credential to exclude from the registration process
The Authenticator Selection Criteria
Attestation conveyance preference
Extensions
Let’s see an example of the PublicKeyCredentialCreationOptions
object. The following example is a possible Public Key Creation page for a dummy user "@cypher-Angel-3000".
It is important to store the user entity and the options object (e.g. in the session) for the next step. The data will be needed to check the response from the device.
You can change the default values for each and all options
What you receive must be a JSON object that looks like as follow:
There are two steps to perform with this object:
Load the data
Verify it with the creation options set above
Now that all components are set, we can load the data we receive using the Public Key Credential Loader service (variable $publicKeyCredential
).
If no exception is thrown, you can go to the next step: the verification.
Now we have a fully loaded Public Key Credential object, but we need now to make sure that:
The authenticator response is of type AuthenticatorAttestationResponse
This response is valid.
The first step is easy to perform:
The second step is the verification against
The Public Key Creation Options we created earlier,
The HTTP request
The Authenticator Attestation Response Validator service (variable $authenticatorAttestationResponseValidator
) will check everything for you: challenge, origin, attestation statement and much more.
The library needs PSR-7 requests. In the example below, we use nyholm/psr7-server
to get that request.
If no exception is thrown, the response is valid. You can store the Public Key Credential Source ($publicKeyCredentialSource
) and associate it to the user entity.
The way you store and associate these objects to the user is out of scope of this library. However, please note that these objects implement \JsonSerializable
and have a static method createFromArray(array $data)
. This will allow you to serialize the objects into JSON and easily go back to an object.
Your application certainly has a user repository. Good news: there is no need to modify it!
The User Entity Repository can be completely decoupled from the user entity used by your application.
This repository should be declared as a Symfony service and shall implement Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepository
.
Hereafter an example where the application User Repository is injected. This repository uses Doctrine and provides findOneBy*
methods.
The following example is totally fictive. We will add an extension input loc=true
to the request option object.
An Extension Output Checker will check the extension output.
It must implement the interface Webauthn\AuthenticationExtensions\ExtensionOutputChecker
and throw an exception of type Webauthn\AuthenticationExtension\ExtensionOutputError
in case of error.
Devices may ignore the extension inputs. The extension outputs are therefore not guaranteed.
In the previous example, we asked for the location of the device and we expect to receive geolocation data in the extension output.
How to register and authenticate my users?
To authenticate or register your users with Symfony, the best and easiest way is to use the Security Bundle. First, install that bundle and follow the instructions given by .
At the end of the installation and configuration, you should have a config/packages/security.yaml
file that looks like as follow:
To enable the user authentication, you just have to declare the webauthn authenticator if the appropriate firewall (here main
).
As you have noticed, there is nothing to configure to have a fully functional firewall. The firewall routes are automatically created for you. They are namely:
/login/options
: to create the request options (POST
only)
/login
: to submit the assertion response (POST
only)
Note that the endpoint shall be secured (https
)
You should also ensure to allow anonymous users to contact those endpoints.
If you need, you can customize those endpoints (do not forget to allow anonymous access).
The user registration can also managed by the firewall. It is disabled by default. If you want that feature, please enable it:
The firewall routes are automatically created for you. They are namely:
/register/options
: to create the creation options (POST only)
/register
: to submit the attestation response (POST only)
You should also ensure to allow anonymous users to contact those endpoints.
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 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. In the example below, the /admin path can be reached by users with the role ROLE_ADMIN
and that have been verified during the ceremony.
You can customize the responses returned by the firewall by using a custom handler. This could be useful when you want to return additional information to your application.
There are 4 types of responses and handlers:
Request options: options returned during the authentication ceremony,
Creation options: options returned during the registration ceremony,
Authentication Success,
Authentication Failure,
This handler is called when a client sends a valid POST request to the options_path
during the authentication process. 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 has 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 very similar to the previous one, except that it is called during the registration of a new user and has to implement the interface Webauthn\Bundle\Security\Handler\CreationOptionsHandler
.
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 has 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 has 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:
Where the public keys and details are stored
The Credential Source can be stored the way you want. As the Webauthn\PublicKeyCredentialSource
class can be converted into JSON, it could be stored in a filesystem.
In general, Symfony applications use Doctrine. That is why the bundle provides a way to use Doctrine as storage system.
Hereafter an example of an entity.
This is the most simple example. Feel free to add custom fields that fits on your needs e.g. created_at
or is_revoked
.
Do not forget to update your database schema!
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 autowiring and autoconfiguration, this is usually done automatically
The authenticators may have an internal counter. This feature is very helpful to detect cloned devices.
The default behaviour is to reject the assertions. This behaviour might cause some troubles as it could reject the real device whilst the fake one can continue to be used.
It is therefore required to go deeper in the protection of your application by logging the error and locking the associated account.
To do so , you have to create a custom Counter Checker and inject it to your Authenticator Assertion Response Validator. The checker must implement the interface Webauthn\Counter\CounterChecker
.
This framework provides a Symfony bundle that will help you to use the components within your Symfony application.
If you use Laravel, you may be interested in
Symfony Flex recipes are provided through a dedicated repository. It is highly recommended to add this repository before installing the bundle.
Then, you can install the bundle or the complete framework:
As the recipes are third party ones not officially supported by Symfony, you will be asked to execute the recipe or not. When applied, the recipes will prompt a message such as WebAuthn Framework is ready.
If you don't use Symfony Flex, you must register the bundle manually.
The first steps are:
The options object can be converted into JSON and sent to the authenticator .
By default, the default
profile is used (see request_profiles
in the ). You may have created a request profile in the bundle configuration. You can use this profile instead of the default one.
The creation of your
The creation of your .
You may also want to configure the other options offered by the bundle. Please refer to the .
Now you have a fully configured bundle, you can protect your routes and manage the user registration and authenticatin through the .
If you have troubles during the development of your application or if you want to keep track of every critical/error messages in production, you can use a PSR-3 compatible logger.
Disclaimer: you should not ask for the Attestation Statement unless you are working on an application that requires a high level of trust (e.g. Banking/Financial Company, Government Agency...).
With Symfony, you must enable this feature to enable all the metadata types.
You can set the Google API key for the Android SafetyNet Attestation Statement support with the following configuration:
If you have some troubles when validating Android SafetyNet Attestation Statement, this may be caused by the leeway of the server clocks or the age of the statement. You can modify the default values as follows:
The modification of these parameters is not recommended. You should try to sync your server clock first.
By default, no Attestation Statement is asked to the Authenticators (type = none
). To change this behavior, you just have to set the corresponding parameter in the Webauthn\PublicKeyCredentialCreationOptions
object.
There are 3 conveyance modes available using PHP constants provided by the class Webauthn\PublicKeyCredentialCreationOptions
:
ATTESTATION_CONVEYANCE_PREFERENCE_NONE
: the Relying Party is not interested in authenticator attestation (default)
ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT
: the Relying Party prefers an attestation conveyance yielding verifiable attestation statements, but allows the client to decide how to obtain such attestation statements.
ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT
: the Relying Party wants to receive the attestation statement as generated by the authenticator.
With Flex, you have a minimal configuration file installed through a Flex Recipe. You must set the repositories you have just created. You also have to modify the environment variables Relying_PARTY_ID
and Relying_PARTY_NAME
.
You may also need to adjust other parameters.
If you don’t use Flex, hereafter an example of configuration file:
If you don't create the creation_profiles
section, a default
profile is set.
The relying Party corresponds to your application. Please refer to this page for more information.
The parameter id
is optional but highly recommended.
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 follows:
For v4.0+, the timeout is set to null
. The values recommended by the specification are as follow:
If the user verification is discouraged
, timeout should be between 30 and 180 seconds
If the user verification is preferred
or required
, the range is 300 to 600 seconds (5 to 10 minutes)
These behaviors are not necessarily followed by the web browsers.
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 changing 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.
The use of Attestation Statements is generally not recommended unless you REALLY need this information.
You can set as many extensions as you want in the profile. Please also refer to this page for more information.
The example below is totally fictive. Some extensions are defined in the specification but the support depends on the authenticators, on the browsers and on the relying parties (your applications).
If you don't create the creation_profiles
section, a default
profile is set.
The parameters for the request profiles (i.e. the authentication) are very similar to the creation profiles. The only difference is that you don’t need all the detail of the Relying Party, but only its ID (i.e. its domain).
Please note that all parameters are optional. The following configuration is perfectly valid. However, and as mentioned above, the parameter id
is highly recommended.
The easiest way to manage the user verification requirements in your Symfony application is by using the creation and request profiles.
In some circumstances, you may need to register a new authenticator for a user e.g. when adding a new authenticator or when an administrator acts as another user to replace a lost device.
It is possible to perform this ceremony programmatically.
You can attach several authenticators to a user account. It is recommended in case of lost devices or if the user gets access on your application using multiple platforms (smartphone, laptop…).
With a Symfony application, the fastest way for a user to register additional authenticators is to use the “controller” feature.
To add a new authenticator to a user, the bundle needs to know to whom it should be added. This can be:
The current user itself e.g. from its own account
An administrator acting for another user from a dashboard
For that purpose, a User Entity Guesser service should be created. This service shall implement the interface Webauthn\Bundle\Security\Guesser\UserEntityGuesser
and its unique method findUserEntity
.
You can directly use the Webauthn\Bundle\Security\Guesser\CurrentUserEntityGuesser
as a Symfony service. It is designed to identify the user that is currently logged in.
In the example herafter where the current user is guessed using a controller parameter. This can be used when an administrator is adding an authenticator to another user account.
In the case the current user s supposed to be administrator, the user entity can be determined using the query parameters and a route like /admin/add-authenticator/for/{user_id}
.
Now you just have to enable the feature and set the routes to your options and response controllers.
As the user shall be authenticated to register a new authenticator, you should protect these routes in the security.yaml
file.
Now you can send requests to these new endpoints. For example, if you are using the Javascript library, the calls will look like as follow:
The default
creation profile is used. You can change it using the dedicated option.
You can customize the responses returned by the controllers by using custom handlers. This could be useful when you want to return additional information to your application.
There are 3 types of responses and handlers:
Creation options,
Success,
Failure.
This handler is called during the registration of a authenticator and has to implement the interface Webauthn\Bundle\Security\Handler\CreationOptionsHandler
.
This handler is called when a client sends a valid assertion from the authenticator. This handler shall implement the interface Webauthn\Bundle\Security\Handler\SuccessHandler
. The default handler is Webauthn\Bundle\Service\DefaultSuccessHandler
.
This handler is called when an error occurred during the process. This handler shall implement the interface Webauthn\Bundle\Security\Handler\SuccessHandler
. The default handler is Webauthn\Bundle\Service\DefaultFailureHandler
.
By default, any type of authenticator can be used by your users and interact with you application. In certain circumstances, you may need to select specific authenticators e.g. when user verification is required.
The Webauthn API and this library allow you to define a set of options to disallow the registration of authenticators that do not fulfill with the conditions.
The class Webauthn\AuthenticatorSelectionCriteria
is designed for this purpose. It is used when generating the Webauthn\PublicKeyCredentialCreationOptions
object.
You can indicate if the authenticator must be attached to the client (platform authenticator i.e. it is usually not removable from the client device) or must be detached (roaming authenticator).
Possible values are:
AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_NO_PREFERENCE
: there is no requirement (default value),
AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_PLATFORM
: the authenticator must be attached,
AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM
: must be a roaming authenticator.
A primary use case for platform authenticators is to register a particular client device as a "trusted device" for future authentication. This gives the user the convenience benefit of not needing a roaming authenticator, e.g., the user will not have to dig around in their pocket for their key fob or phone.
When this criterion is set to true
, a Public Key Credential Source will be stored in the authenticator, client or client device. Such storage requires an authenticator capable to store such a resident credential.
This criterion is needed if you want to authenticate users without username.
To be written
With Webauthn, it is possible to authenticate a user without username. This behavior implies several constraints:
During the registration of the authenticator, a Resident Key must have been asked,
The user verification is required,
The list of allowed authenticators must be empty
In case of failure, you should continue with the standard authentication process i.e. by asking the username of the user.
The bundle configuration should have a profile with the constraints listed above:
Step-by-step guide for migrating from v3.x to v4.0
This project follows the Semantic Versioning principles and, contrary to upgrade a minor version (where the middle number changes) where no difficulty should be encountered, upgrade a major version (where the first number changes) is subject to significant modifications.
First of all, you have to make sure you are using the last v3.x release (v3.3.4 at the time of writing).
In addition, you have to make sure you are using PHP 8.1+
.
If you use the Symfony bundle and Symfony Flex, please note that Flex Recipes are now provided through a dedicated server.
It is highly recommended to declare this server within your application composer.json
file.
You can adapt this command line depending on the other Flex servers you are using.
Next, you have to verify you don’t use any deprecated class, interface, method or property. If you have PHPUnit tests, you can easily get the list of deprecation used in your application.
The class Webauthn\Server
is removed and there is no replacement.
The Metadata Statement embraces the version 3 of the specification. There is no migration tool to convert the MDS from v2 to v3. We suggest to refer to this blog post from Yuriy Ackermann, a Fido Alliance staff member who regulary write articles on Webauthn.
Added:
symfony/http-client ^6.0
symfony/uid ^6.0
Bumped:
spomky-labs/cbor-php
minimal version is now ^3.0
spomky-labs/cbor-bundle
minimal version is now ^3.0
symfony
components minimal version is now ^6.0
symfony/psr-http-message-bridge
minimal version is now ^2.0
thecodingmachine/safe minimal version is now ^2.0
Removed:
sensio/framework-extra-bundle
ramsey/uuid
As the bundle sensio/framework-extra-bundle
is not required anymore, the associated configuration may become useless if you do not use this bundle in your project.
The most notable changes are related to the Metadata Statement section
It is now time to upgrade the libraries. In your composer.json, change all web-auth/*
dependencies from v3.x
to v4.0
. When done, execute composer update
.
This may also update other dependencies. You can list upgradable libraries by calling composer outdated
. Please make sure these libraries do not impact your upgrade.
If you want to see all modifications at once, please have a look at this page.
To change the token binding behaviour, you can change the dedicated parameter in the bundle configuration.
An Extension Output Checker will check the extension output.
It must implement the interface Webauthn\AuthenticationExtensions\ExtensionOutputChecker
and throw an exception of type Webauthn\AuthenticationExtension\ExtensionOutputError
in case of error.
Devices may ignore the extension inputs. The extension outputs are therefore not guaranteed.
In the previous example, we asked for the location of the device and we expect to receive geolocation data in the extension output.
The easiest way to manage that is by using the creation and request profiles.
User verification may be instigated through various authorization gesture modalities: a touch plus PIN code, password entry, or biometric recognition (presenting a fingerprint). The intent is to be able to distinguish individual users.
Eligible authenticators are filtered and only capable of satisfying this requirement will interact with the user.
Possible user verification values are:
required
: this value indicates that the application requires user verification for the operation and will fail the operation if the response does not have the UV
flag set.
preferred
: this value indicates that the application prefers user verification for the operation if possible, but will not fail the operation if the response does not have the UV
flag set.
discouraged
: this value indicates that the application does not want user verification employed during the operation (e.g.,in the interest of minimizing disruption to the user interaction flow).
Public constants are provided by AuthenticatorSelectionCriteria
.
AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED
AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED
AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED
The authenticators may have an internal counter. This feature is very helpful to detect cloned devices.
The default behaviour is to reject the assertions. This might cause some troubles as it could reject the real device whilst the fake one can continue to be used. You may also want to log the error, warn administrators or lock the associated user account.
To do so , you have to create a custom Counter Checker and inject it to your Authenticator Assertion Response Validator. The checker must implement the interface Webauthn\Counter\CounterChecker
.
The following example is fictive and show how to lock a user, log the error and throw an exception.
If your are working on a development environment, https
may not be available but the context could be considered as secured. You can bypass the scheme verification by passing the list of rpIds you consider secured.
Please be careful using this feature. It should NOT be used in production.