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...
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 PSR-12 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.
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 Pull Requests, 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
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…).
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
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.
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 the W3C dedicated page.
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 section 5.2.2 “Web Authentication Assertion” shows an object named AuthenticatorAssertionResponse
that extends AuthenticatorResponse
with the following properties:
authenticatorData
signature
userHandle
You will find EXACTLY the same structure 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.
It is also tested using the official FIDO Alliance testing tools. The status of the compliance tests are reported in this issue. At the time of writing (end of April 2021), the main features and algorithms are supported and 99% of the tests pass.
I bring solutions to your problems and answer your questions.
If you really love that project, and the work I have done or if you want I prioritize your issues, then you can help me out for a couple of🍻 or more!
Requests for new features, bug fixed and all other ideas to make this framework useful are welcome.
If you feel comfortable writing code, you could try to fix opened issues where help is wanted or those that are easy to fix.
Do not forget to follow these best practices.
If you think you have found a security issue, DO NOT open an issue. You MUST submit your issue here.
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
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.
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 RFC 8471). 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
.
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 https://www.iana.org/assignments/webauthn/webauthn.xhtml
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.
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.
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.
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:
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.
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.
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
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.