diff --git a/docs/overrides/partials/logo.html b/docs/overrides/partials/logo.html
new file mode 100644
index 0000000..e261fcf
--- /dev/null
+++ b/docs/overrides/partials/logo.html
@@ -0,0 +1,8 @@
+{#-
+ The project mark of WebAuthn Framework: the Slabs monogram, the name of the project and "by Slabs". The
+ colour version on a light ground, the reversed one on a dark ground, from the web set of the design kit
+ without their metadata (tools/web-svg.py of Spomky-Labs/docs). The link around them carries the
+ accessible name, so the images say nothing.
+-#}
+
+
diff --git a/docs/overrides/partials/source.html b/docs/overrides/partials/source.html
new file mode 100644
index 0000000..3d8f7f0
--- /dev/null
+++ b/docs/overrides/partials/source.html
@@ -0,0 +1,13 @@
+{#-
+ The link to the repository, without the stars and the forks: the theme would ask the API of GitHub for
+ them from the browser of the reader, and this site sends nothing to a third party.
+-#}
+
+
+
diff --git a/prerequisites/credential-record.md b/docs/prerequisites/credential-record.md
similarity index 76%
rename from prerequisites/credential-record.md
rename to docs/prerequisites/credential-record.md
index f05a996..8c766d5 100644
--- a/prerequisites/credential-record.md
+++ b/docs/prerequisites/credential-record.md
@@ -2,9 +2,9 @@
description: Authenticator details and how to manage them
---
-# Credential Record
+# Credential record
-## Credential Record Class
+## Credential record class
After the registration of an authenticator, you will get a `Webauthn\CredentialRecord` object. This object contains all the credential data needed to perform user authentication:
@@ -22,12 +22,11 @@ After the registration of an authenticator, you will get a `Webauthn\CredentialR
* **uvInitialized**: Whether user verification was initialized (since 5.1)
* **otherUI**: Optional additional UI hints (array)
-{% hint style="warning" %}
-**Renamed in v5.3.0:** The class `Webauthn\PublicKeyCredentialSource` has been renamed to `Webauthn\CredentialRecord`. The old class name is deprecated and will be removed in version 6.0. `PublicKeyCredentialSource` now extends `CredentialRecord` for backward compatibility.
-{% endhint %}
+!!! warning
-{% code lineNumbers="true" %}
-```php
+ **Renamed in v5.3.0:** The class `Webauthn\PublicKeyCredentialSource` has been renamed to `Webauthn\CredentialRecord`. The old class name is deprecated and will be removed in version 6.0. `PublicKeyCredentialSource` now extends `CredentialRecord` for backward compatibility.
+
+```php linenums="1"
transports; // ['usb', 'nfc']
// Get the descriptor for authentication
$descriptor = $credentialRecord->getPublicKeyCredentialDescriptor();
```
-{% endcode %}
-## Credential Record Repository
+## Credential record repository
Since 4.6.0 and except if you use the Symfony bundle, there is no interface to implement or abstract class to extend, making it easy to integrate into your application.
@@ -61,16 +59,15 @@ Your repository needs to provide two main operations:
1. **Save a credential** after registration or counter update
2. **Find credentials** by credential ID or user handle
-{% hint style="success" %}
-Whatever database you use (MySQL, PostgreSQL, MongoDB…), it is not necessary to create foreign key relationships between your users and the Credential Records. The `userHandle` property is sufficient to link credentials to users.
-{% endhint %}
+!!! success
+
+ Whatever database you use (MySQL, PostgreSQL, MongoDB…), it is not necessary to create foreign key relationships between your users and the Credential Records. The `userHandle` property is sufficient to link credentials to users.
-### Repository Example
+### Repository example
Here's a simple example using an array storage (for demonstration purposes):
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
add(PackedAttestationStatementSupport::crea
use Webauthn\AttestationStatement\CompoundAttestationStatementSupport;
$attestationStatementSupportManager->add(CompoundAttestationStatementSupport::create($attestationStatementSupportManager));
```
-{% endcode %}
-{% hint style="warning" %}
-**Changed in v5.3.6:** `AndroidKeyAttestationStatementSupport` verifies the authorization lists of the attestation certificate. The key must have been generated inside the keystore (`origin` equal to `KM_ORIGIN_GENERATED`) and be allowed to sign (`purpose` containing `KM_PURPOSE_SIGN`). The union of the `softwareEnforced` and `teeEnforced` lists is used, as described by the specification. An imported key, accepted by the previous versions, is now rejected.
-{% endhint %}
+!!! warning
+
+ **Changed in v5.3.6:** `AndroidKeyAttestationStatementSupport` verifies the authorization lists of the attestation certificate. The key must have been generated inside the keystore (`origin` equal to `KM_ORIGIN_GENERATED`) and be allowed to sign (`purpose` containing `KM_PURPOSE_SIGN`). The union of the `softwareEnforced` and `teeEnforced` lists is used, as described by the specification. An imported key, accepted by the previous versions, is now rejected.
-{% hint style="info" %}
-**Compound Attestation Statement (v5.3.0+):** The Compound Attestation format allows multiple attestation statements to be bundled together. This is useful for authenticators that support multiple attestation mechanisms. The `CompoundAttestationStatementSupport` requires the `AttestationStatementSupportManager` to process the individual attestation statements within the compound format.
-{% endhint %}
+!!! info
-{% hint style="warning" %}
-The Android SafetyNet Attestation API is deprecated. Full turndown is planned in June 2024. More information at [https://developer.android.com/training/safetynet/deprecation-timeline](https://developer.android.com/training/safetynet/deprecation-timeline)
+ **Compound Attestation Statement (v5.3.0+):** The Compound Attestation format allows multiple attestation statements to be bundled together. This is useful for authenticators that support multiple attestation mechanisms. The `CompoundAttestationStatementSupport` requires the `AttestationStatementSupportManager` to process the individual attestation statements within the compound format.
-It is not described on this page, but available in the previous versions of the documentation.
-{% endhint %}
+!!! warning
-## Metadata Statement Repository
+ The Android SafetyNet Attestation API is deprecated. Full turndown is planned in June 2024. More information at [https://developer.android.com/training/safetynet/deprecation-timeline](https://developer.android.com/training/safetynet/deprecation-timeline)
+
+ It is not described on this page, but available in the previous versions of the documentation.
+
+## Metadata statement repository
Then, you must prepare a Metadata Statement Repository. This service will manage all Metadata Statements depending on their sources (local storage or distant service).
@@ -83,11 +81,11 @@ Your Metadata Statement Repository must implement the interface `Webauthn\Metada
* `findOneByAAGUID(string $aaguid)`: this method retrieves the `MetadataStatement` object with AAGUID. It shall return `null` in case of the absence of the MDS.
-{% hint style="warning" %}
-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.
-{% endhint %}
+!!! warning
+
+ 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.
-## Status Report Repository
+## Status report repository
To prevent the use of rogue MDS or deprecated by the manufacturers, Status Reports may be used. The Status Report Repository is a simple service that implements the interface `Webauthn\MetadataService\StatusReportRepository` with a unique method:
@@ -95,14 +93,13 @@ To prevent the use of rogue MDS or deprecated by the manufacturers, Status Repor
The verification's success or failure depends on the state reported in the most recent Status Report.
-## Certificate Chain Validator
+## Certificate chain validator
When an attestation statement is received, a certificate chain is constructed. This chain combines certificates from the Metadata Service (MDS), which are trusted, with those provided by the authenticator, which are untrusted.
The Certificate Chain Validator is responsible for this task. The library provides the class `Webauthn\MetadataService\CertificateChain\PhpCertificateChainValidator` that requires a Symfony Http Client ([for CRL verification](https://en.wikipedia.org/wiki/Certificate_revocation_list)) and a PSR-20 Clock.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
enableMetadataStatementSupport(
$certificateChainValidator,
);
```
-{% endcode %}
-## Requesting Attestation Statement
+## Requesting attestation statement
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.
@@ -146,8 +140,7 @@ There are 3 conveyance modes available using PHP constants provided by the class
* `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.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
setAlgorithmManager($algorithmManager);
```
-{% endcode %}
diff --git a/pure-php/advanced-behaviours/authenticator-counter.md b/docs/pure-php/advanced-behaviours/authenticator-counter.md
similarity index 92%
rename from pure-php/advanced-behaviours/authenticator-counter.md
rename to docs/pure-php/advanced-behaviours/authenticator-counter.md
index d846207..84f62d2 100644
--- a/pure-php/advanced-behaviours/authenticator-counter.md
+++ b/docs/pure-php/advanced-behaviours/authenticator-counter.md
@@ -1,4 +1,4 @@
-# Authenticator Counter
+# Authenticator counter
The authenticators may have an internal counter. This feature is very helpful to detect cloned devices.
@@ -8,13 +8,11 @@ It is therefore required to go deeper in the protection of your application by l
To do so, you have to create a custom Counter Checker and inject it into your Authenticator Assertion Response Validator. The checker must implement the interface `Webauthn\Counter\CounterChecker`.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
setCounterChecker($customCounterChecker);
```
-{% endcode %}
diff --git a/pure-php/advanced-behaviours/authenticator-selection-criteria.md b/docs/pure-php/advanced-behaviours/authenticator-selection-criteria.md
similarity index 79%
rename from pure-php/advanced-behaviours/authenticator-selection-criteria.md
rename to docs/pure-php/advanced-behaviours/authenticator-selection-criteria.md
index 2996641..e46532f 100644
--- a/pure-php/advanced-behaviours/authenticator-selection-criteria.md
+++ b/docs/pure-php/advanced-behaviours/authenticator-selection-criteria.md
@@ -1,4 +1,4 @@
-# Authenticator Selection Criteria
+# Authenticator selection criteria
By default, any type of authenticator can be used by your users and interact with your application. In certain circumstances, you may need to select specific authenticators e.g. when user verification is required.
@@ -6,9 +6,9 @@ The WebAuthn API and this library allow you to define a set of options to disall
The class `Webauthn\AuthenticatorSelectionCriteria` is designed for this purpose. It is used when generating the `Webauthn\PublicKeyCredentialCreationOptions` object.
-## Available Criteria
+## Available criteria
-### Authenticator Attachment Modality
+### Authenticator attachment modality
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).
@@ -20,19 +20,19 @@ Possible values are:
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.
-### Resident Key
+### Resident key
With this criterion, a credential record will be stored in the authenticator, client or client device. Such storage requires an authenticator capable to store such a resident credential.
-{% hint style="info" %}
-A resident key shall be created if you want to [authenticate users without username](authentication-without-username.md).
-{% endhint %}
+!!! info
-{% hint style="warning" %}
-**Backward Compatibility (v5.3.0+):** The `requireResidentKey` property has been restored for backward compatibility with WebAuthn Level 3 specification. While `residentKey` is the preferred modern approach, `requireResidentKey` is still supported for legacy implementations.
-{% endhint %}
+ A resident key shall be created if you want to [authenticate users without username](authentication-without-username.md).
-### User Verification
+!!! warning
+
+ **Backward Compatibility (v5.3.0+):** The `requireResidentKey` property has been restored for backward compatibility with WebAuthn Level 3 specification. While `residentKey` is the preferred modern approach, `requireResidentKey` is still supported for legacy implementations.
+
+### User verification
[Please refer to this page](user-verification.md).
@@ -40,8 +40,7 @@ A resident key shall be created if you want to [authenticate users without usern
With this example, we require the user verification (PIN, fingerprint...), a resident key and an authenticator embedded onto a device. This is typically what you will require for Windows Hello or Face ID authentication.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
use Webauthn\AuthenticatorSelectionCriteria;
use Webauthn\PublicKeyCredentialCreationOptions;
@@ -59,4 +58,3 @@ $publicKeyCredentialCreationOptions =
authenticatorSelection: $authenticatorSelectionCriteria
);
```
-{% endcode %}
diff --git a/pure-php/advanced-behaviours/backup-events.md b/docs/pure-php/advanced-behaviours/backup-events.md
similarity index 82%
rename from pure-php/advanced-behaviours/backup-events.md
rename to docs/pure-php/advanced-behaviours/backup-events.md
index dac2029..02181ad 100644
--- a/pure-php/advanced-behaviours/backup-events.md
+++ b/docs/pure-php/advanced-behaviours/backup-events.md
@@ -1,8 +1,8 @@
-# Backup Events
+# Backup events
-{% hint style="info" %}
-**New in v5.3.0**
-{% endhint %}
+!!! info
+
+ **New in v5.3.0**
The framework dispatches PSR-14 events when the backup eligibility or backup status flags of a credential change during authentication. These events allow you to react to changes in credential backup state, which is important for security monitoring and user guidance.
@@ -13,9 +13,9 @@ WebAuthn authenticators report two backup-related flags:
* **BE (Backup Eligible)**: Indicates whether the authenticator is capable of backing up the credential (e.g., synced passkeys via iCloud Keychain, Google Password Manager)
* **BS (Backup Status)**: Indicates whether the credential is currently backed up
-{% hint style="info" %}
-**Fixed in v5.3.6:** `AuthenticatorData::getReservedForFutureUse2()` reported the bits 3 and 4 as reserved, although WebAuthn Level 3 assigns them to BE and BS. A synced passkey with both flags set returned `24` instead of `0`. The mask is now restricted to the only bit that is still reserved. Use `isBackupEligible()` and `isBackedUp()` to read the backup flags.
-{% endhint %}
+!!! info
+
+ **Fixed in v5.3.6:** `AuthenticatorData::getReservedForFutureUse2()` reported the bits 3 and 4 as reserved, although WebAuthn Level 3 assigns them to BE and BS. A synced passkey with both flags set returned `24` instead of `0`. The mask is now restricted to the only bit that is still reserved. Use `isBackupEligible()` and `isBackedUp()` to read the backup flags.
Changes in these flags can signal important security events:
@@ -28,8 +28,7 @@ Changes in these flags can signal important security events:
Dispatched when the backup eligibility (BE) flag changes between authentications.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
addListener(
new BackupStatusListener()
);
```
-{% endcode %}
### Symfony
With the Symfony bundle, register your listeners as services. With autoconfiguration, they are registered automatically.
-{% code title="config/services.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/services.yaml" linenums="1"
services:
App\EventListener\BackupStatusListener:
tags:
@@ -122,15 +115,14 @@ services:
tags:
- { name: kernel.event_listener, event: Webauthn\Event\BackupEligibilityChangedEvent }
```
-{% endcode %}
-## Security Considerations
+## Security considerations
* **Monitor backup status loss**: If a credential transitions from backed up to not backed up, the user may have lost their backup. Consider prompting them to register additional authenticators.
* **Audit backup eligibility changes**: Unexpected changes in backup eligibility may indicate authenticator changes worth logging.
* **Do not block authentication**: These events are informational. Do not reject authentication attempts based on backup flag changes.
-## See Also
+## See also
* [Credential Record](../../prerequisites/credential-record.md) - Credential properties including backup flags
* [Authenticator Counter](authenticator-counter.md) - Another security monitoring mechanism
diff --git a/pure-php/advanced-behaviours/conditional-create.md b/docs/pure-php/advanced-behaviours/conditional-create.md
similarity index 77%
rename from pure-php/advanced-behaviours/conditional-create.md
rename to docs/pure-php/advanced-behaviours/conditional-create.md
index c722cbd..6c29dcd 100644
--- a/pure-php/advanced-behaviours/conditional-create.md
+++ b/docs/pure-php/advanced-behaviours/conditional-create.md
@@ -1,20 +1,20 @@
-# Conditional Create
+# Conditional create
-{% hint style="info" %}
-**New in v5.3.0**
-{% endhint %}
+!!! info
+
+ **New in v5.3.0**
Conditional Create allows you to register a WebAuthn credential without explicit user interaction, typically after a user has already authenticated via another method (e.g., password login). This enables a seamless upgrade path from traditional authentication to passkeys.
-## How It Works
+## How it works
In a standard WebAuthn registration ceremony, user presence is always required (the user must interact with the authenticator). With Conditional Create (`mediation: 'conditional'`), the browser can silently create a credential after the user has already proven their identity through another means.
Because the credential is created without any interaction, the User Present (UP) and User Verified (UV) flags of the authenticator data may both be `false`. The validation relaxes the two checks accordingly.
-{% hint style="info" %}
-**Changed in v5.3.6:** the User Verification check is relaxed as well. Until then only the User Presence check was, so a conditional registration made against a profile requiring user verification was rejected with `User authentication required.`.
-{% endhint %}
+!!! info
+
+ **Changed in v5.3.6:** the User Verification check is relaxed as well. Until then only the User Presence check was, so a conditional registration made against a profile requiring user verification was rejected with `User authentication required.`.
This is particularly useful for:
@@ -22,16 +22,15 @@ This is particularly useful for:
* **Progressive enrollment**: Gradually migrate users from passwords to passkeys
* **Background registration**: Register credentials without interrupting the user flow
-## Pure PHP Usage
+## Pure PHP usage
There are two equivalent ways to relax the User Presence (UP) and User Verification (UV) checks during validation: a per-request hint on the options, or a dedicated ceremony manager. The first option is the recommended one since v5.3.0 because the hint travels with the options across the storage round-trip and applies automatically.
-### Option 1 — Set `mediation` on the options (recommended)
+### Option 1 — set `mediation` on the options (recommended)
`PublicKeyCredentialCreationOptions` exposes two constants, `MEDIATION_DEFAULT` and `MEDIATION_CONDITIONAL`, and a nullable `$mediation` property. When the property is set to `MEDIATION_CONDITIONAL`, `CheckUserWasPresent` skips the UP check and `CheckUserVerification` skips the UV check at runtime, regardless of which ceremony manager you use.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
creationCeremony();
// Conditional create ceremony (user presence can be false)
$conditionalCeremony = $csmFactory->conditionalCreateCeremony();
```
-{% endcode %}
Use the conditional ceremony manager when validating attestation responses from conditional create flows:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
check(
'example.com'
);
```
-{% endcode %}
-## Symfony Bundle Configuration
+## Symfony bundle configuration
Enable conditional create per creation profile. The bundle's `CeremonyStepManagerFactory` translates the `conditional_create: true` flag into `mediation = 'conditional'` on the produced options, so existing 5.3.x configurations behave identically:
-{% code title="config/packages/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/webauthn.yaml" linenums="1"
webauthn:
creation_profiles:
passkey_upgrade:
@@ -112,12 +105,10 @@ webauthn:
require_resident_key: true
user_verification: preferred
```
-{% endcode %}
If you would rather decide per request — for example, only enable conditional create when the JavaScript client explicitly asks for it — opt in via the [Client Override Policy](../../symfony-bundle/advanced-behaviors/client-override-policy.md):
-{% code title="config/packages/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/webauthn.yaml" linenums="1"
webauthn:
creation_profiles:
passkey_upgrade:
@@ -128,20 +119,17 @@ webauthn:
enabled: true
allowed_values: ['default', 'conditional']
```
-{% endcode %}
-## Frontend Integration
+## Frontend integration
On the frontend, use `mediation: 'conditional'` when calling `navigator.credentials.create()`:
-{% code lineNumbers="true" %}
-```javascript
+```javascript linenums="1"
const credential = await navigator.credentials.create({
publicKey: creationOptions,
mediation: 'conditional'
});
```
-{% endcode %}
With the Stimulus controller, use the `autoRegister` option on the registration controller:
@@ -153,11 +141,11 @@ With the Stimulus controller, use the `autoRegister` option on the registration
}) }}
```
-{% hint style="warning" %}
-Conditional Create requires browser support. Not all browsers support this feature yet. Always provide a fallback registration flow.
-{% endhint %}
+!!! warning
+
+ Conditional Create requires browser support. Not all browsers support this feature yet. Always provide a fallback registration flow.
-## See Also
+## See also
* [Register Authenticators](../../pure-php/authenticator-registration.md) - Standard registration ceremony
* [Stimulus Controllers](../../symfony-ux/installation.md) - Frontend integration
diff --git a/pure-php/advanced-behaviours/cross-origin-authentication.md b/docs/pure-php/advanced-behaviours/cross-origin-authentication.md
similarity index 70%
rename from pure-php/advanced-behaviours/cross-origin-authentication.md
rename to docs/pure-php/advanced-behaviours/cross-origin-authentication.md
index cad6998..3ee06f2 100644
--- a/pure-php/advanced-behaviours/cross-origin-authentication.md
+++ b/docs/pure-php/advanced-behaviours/cross-origin-authentication.md
@@ -1,4 +1,4 @@
-# Cross Origin Authentication
+# Cross origin authentication
## Default behavior
@@ -9,22 +9,21 @@ By default, the current host is used, and the origin parameter returned in the W
* **Subdomain authentication**: Some deployments use a wildcard subdomain approach (e.g., `tenant1.example.com`, `tenant2.example.com`), but strict origin checking will reject authentication if the relying party ID does not match exactly.
* **Development and testing environments**: Local development often uses `http://localhost`, which is not allowed by default due to the HTTPS requirement.
-## Customizing Allowed Origins
+## Customizing allowed origins
To handle these cases, you can explicitly define a list of allowed origins when configuring the WebAuthn authentication process using the method `setAllowedOrigins`.
-{% hint style="info" %}
-The method `setSecuredRelyingPartyId` is deprecated since 5.2.0
-{% endhint %}
+!!! info
-### Allowing Multiple Origins
+ The method `setSecuredRelyingPartyId` is deprecated since 5.2.0
+
+### Allowing multiple origins
If your application supports multiple domains, specify them explicitly:
This ensures that authentication requests coming from any of these origins are accepted.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
setAllowedOrigins([
'https://old-acme.com'
]);
```
-{% endcode %}
-### Allowing Subdomains
+### Allowing subdomains
To allow authentication from subdomains dynamically, enable the `allowSubdomains` flag:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
setAllowedOrigins(['https://acme.com'], true);
```
-{% endcode %}
With this setting, authentication requests from `https://sub.acme.com` will be considered valid.
-{% hint style="danger" %}
-Do not use TLD and the sub domain flag together! `$csmFactory->setSecuredRelyingPartyId(['com'], true);`
-{% endhint %}
+!!! danger
+
+ Do not use TLD and the sub domain flag together! `$csmFactory->setSecuredRelyingPartyId(['com'], true);`
-### Supporting Native Applications
+### Supporting native applications
For native applications, origins are different from traditional web URLs. If your application integrates with mobile apps, ensure that relevant origins are included:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
setAllowedOrigins([
'ios:bundle-id://your.bundle.id',
]);
```
-{% endcode %}
This configuration allows authentication responses from web browsers and native applications.
-{% hint style="info" %}
-**Changed in v5.3.3:** an entry that is not a URL, i.e. that has a scheme but no host, such as `android:apk-key-hash:`, is stored as is and compared verbatim with the `origin` field of the client data. Entries made of a host only, such as `acme.com`, are still normalized to `https://acme.com` because WebAuthn requires TLS.
+!!! info
+
+ **Changed in v5.3.3:** an entry that is not a URL, i.e. that has a scheme but no host, such as `android:apk-key-hash:`, is stored as is and compared verbatim with the `origin` field of the client data. Entries made of a host only, such as `acme.com`, are still normalized to `https://acme.com` because WebAuthn requires TLS.
-Copy the facet identifier exactly as the application sends it. Before v5.3.3 these entries went through the host normalization and were turned into unusable origins such as `https://android:apk-key-hash:...`, so they never matched.
-{% endhint %}
+ Copy the facet identifier exactly as the application sends it. Before v5.3.3 these entries went through the host normalization and were turned into unusable origins such as `https://android:apk-key-hash:...`, so they never matched.
-### Enabling `http://localhost` for Development
+### Enabling `http://localhost` for development
During local development, you may need to allow `http://localhost`. Since WebAuthn generally requires HTTPS, this is an exception for local testing. To allow it:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
setAllowedOrigins([
'http://localhost',
]);
```
-{% endcode %}
Be aware that this should only be enabled in non-production environments.
-## Top Origin Validation (Cross-Origin iframes)
+## Top origin validation (cross-origin iframes)
When the ceremony takes place in a cross-origin iframe, for example a page of `auth.example.com` embedded in `app.example.com`, the browser sets `crossOrigin` to `true` in the client data and adds the origin of the embedding page in a `topOrigin` field.
-{% hint style="danger" %}
-**Changed in v5.3.4:** a response that carries a `topOrigin` is rejected unless a `TopOriginValidator` is configured. Previous versions accepted such a response without checking anything, which means any site embedding your ceremony in an iframe was accepted.
+!!! danger
-Applications that do not use iframes have nothing to do: a same-origin ceremony has no `topOrigin` at all. Applications that are legitimately embedded must opt in with one of the validators below, otherwise their ceremonies now fail.
-{% endhint %}
+ **Changed in v5.3.4:** a response that carries a `topOrigin` is rejected unless a `TopOriginValidator` is configured. Previous versions accepted such a response without checking anything, which means any site embedding your ceremony in an iframe was accepted.
+
+ Applications that do not use iframes have nothing to do: a same-origin ceremony has no `topOrigin` at all. Applications that are legitimately embedded must opt in with one of the validators below, otherwise their ceremonies now fail.
The step also rejects a response whose client data carries a `topOrigin` while `crossOrigin` is `false`, as the two fields contradict each other.
-### Using the Built-in Host Validator
+### Using the built-in host validator
`HostTopOriginValidator` compares the received top origin with a fixed value:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
enableTopOriginValidator(
new HostTopOriginValidator('https://app.example.com')
);
```
-{% endcode %}
-{% hint style="warning" %}
-The comparison is strict. The expected value is the top origin as the browser sends it, i.e. a serialized origin such as `https://app.example.com`, and not a bare host name.
-{% endhint %}
+!!! warning
+
+ The comparison is strict. The expected value is the top origin as the browser sends it, i.e. a serialized origin such as `https://app.example.com`, and not a bare host name.
-### Using a Custom Validator
+### Using a custom validator
When several parent origins are allowed, implement the `TopOriginValidator` interface:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
enableTopOriginValidator(
new AllowedTopOriginsValidator([
'https://app.example.com',
@@ -196,11 +183,10 @@ $csmFactory->enableTopOriginValidator(
])
);
```
-{% endcode %}
A validator set on the factory applies to both the creation and the request ceremonies. `disableTopOriginValidator()` removes it, which is useful when the same factory is reused to build a manager for a context where iframes are not allowed.
-## Security Considerations
+## Security considerations
When modifying allowed origins, ensure that:
diff --git a/pure-php/advanced-behaviours/debugging.md b/docs/pure-php/advanced-behaviours/debugging.md
similarity index 88%
rename from pure-php/advanced-behaviours/debugging.md
rename to docs/pure-php/advanced-behaviours/debugging.md
index dfae766..377be3f 100644
--- a/pure-php/advanced-behaviours/debugging.md
+++ b/docs/pure-php/advanced-behaviours/debugging.md
@@ -2,16 +2,15 @@
WebAuthn operations can be complex to debug due to the cryptographic operations and browser interactions involved. This page explains how to enable logging to troubleshoot issues during development or monitor your application in production.
-## PSR-3 Logger Integration
+## PSR-3 logger integration
The WebAuthn library supports [PSR-3 compatible loggers](https://www.php-fig.org/psr/psr-3/) for debugging and monitoring. You can use any PSR-3 logger implementation such as Monolog, Symfony Logger, or others.
-### Setting Up a Logger
+### Setting up a logger
Several WebAuthn classes implement the `Webauthn\MetadataService\CanLogData` interface, allowing you to inject a PSR-3 logger:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
setLogger($logger);
$assertionValidator = AuthenticatorAssertionResponseValidator::create($requestCSM);
$assertionValidator->setLogger($logger);
```
-{% endcode %}
-## What Gets Logged
+## What gets logged
The WebAuthn library logs various events during authentication and registration:
-### Registration (Attestation) Events
+### Registration (attestation) events
* Challenge generation and validation
* Client data validation
@@ -49,7 +47,7 @@ The WebAuthn library logs various events during authentication and registration:
* Certificate chain validation (when using attestation)
* Errors during any step
-### Authentication (Assertion) Events
+### Authentication (assertion) events
* Challenge validation
* Credential lookup
@@ -58,7 +56,7 @@ The WebAuthn library logs various events during authentication and registration:
* User presence and verification checks
* Errors during any step
-## Log Levels
+## Log levels
The library uses standard PSR-3 log levels:
@@ -68,7 +66,7 @@ The library uses standard PSR-3 log levels:
* **ERROR**: Error messages for failures
* **CRITICAL**: Critical errors requiring immediate attention
-## Example Log Output
+## Example log output
Here's what typical log messages look like:
@@ -86,14 +84,13 @@ In case of errors:
[2025-01-15 10:31:12] webauthn.CRITICAL: Signature verification failed
```
-## Development vs Production
+## Development vs production
-### Development Environment
+### Development environment
Enable verbose DEBUG logging to see all operations:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
pushHandler(
new StreamHandler('php://stdout', LogLevel::DEBUG)
);
```
-{% endcode %}
-### Production Environment
+### Production environment
In production, log only warnings and errors to avoid performance impact:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
pushHandler(
)
);
```
-{% endcode %}
-## Common Issues and Log Messages
+## Common issues and log messages
### "Challenge mismatch"
@@ -154,41 +148,39 @@ $logger->pushHandler(
**Solution**: Check your Relying Party ID configuration matches your domain.
-### "User not present" or "User not verified"
+### "User not present" or "user not verified"
**Cause**: User verification requirements not met.
**Solution**: Check your authenticator selection criteria configuration.
-## Additional Debugging Tools
+## Additional debugging tools
-### Browser Developer Tools
+### Browser developer tools
Modern browsers provide WebAuthn debugging in developer tools:
* **Chrome/Edge**: DevTools → Application → WebAuthn
* **Firefox**: about:config → search for "webauthn"
-### Network Inspector
+### Network inspector
Monitor the network requests to see:
* Options requests (registration/authentication)
* Response data being sent back
* HTTP errors or timeouts
-### Test Authenticators
+### Test authenticators
Use virtual authenticators for testing:
-{% code lineNumbers="true" %}
-```javascript
+```javascript linenums="1"
// Chrome DevTools Protocol
// Enable virtual authenticator in Chrome DevTools → Application → WebAuthn
// Add Virtual Authenticator → Configure options
```
-{% endcode %}
-## See Also
+## See also
* [Symfony Bundle Debugging](../../symfony-bundle/advanced-behaviors/debugging.md) - Framework-specific logging
* [Authenticator Counter](authenticator-counter.md) - Detect cloned authenticators
diff --git a/pure-php/advanced-behaviours/extensions.md b/docs/pure-php/advanced-behaviours/extensions.md
similarity index 88%
rename from pure-php/advanced-behaviours/extensions.md
rename to docs/pure-php/advanced-behaviours/extensions.md
index 89b3e65..73a469d 100644
--- a/pure-php/advanced-behaviours/extensions.md
+++ b/docs/pure-php/advanced-behaviours/extensions.md
@@ -2,8 +2,7 @@
The following example is totally fictive. We will add an extension input `loc=true` to the request option object.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
add(new LocationExtensionOutputChecker());
$csmFactory = new CeremonyStepManagerFactory();
$csmFactory->setExtensionOutputCheckerHandler($eocHandler);
```
-{% endcode %}
diff --git a/pure-php/advanced-behaviours/signal-api.md b/docs/pure-php/advanced-behaviours/signal-api.md
similarity index 90%
rename from pure-php/advanced-behaviours/signal-api.md
rename to docs/pure-php/advanced-behaviours/signal-api.md
index 850fdc7..c1fb99e 100644
--- a/pure-php/advanced-behaviours/signal-api.md
+++ b/docs/pure-php/advanced-behaviours/signal-api.md
@@ -1,12 +1,12 @@
# Signal API
-{% hint style="info" %}
-**New in v5.3.0**
-{% endhint %}
+!!! info
+
+ **New in v5.3.0**
The WebAuthn Signal API allows relying parties to send signals to the client (browser/platform) about credential status. This helps clients maintain accurate credential metadata and improves the user experience by keeping passkey lists up to date.
-## What Is the Signal API?
+## What is the Signal API?
When users manage their passkeys on the server side (removing credentials, updating profile information), the client platform may still display outdated information. The Signal API provides a standardized way to inform the client about:
@@ -14,14 +14,13 @@ When users manage their passkeys on the server side (removing credentials, updat
* Updated user details (name, display name)
* Credentials that are no longer recognized by the server
-## Signal Types
+## Signal types
### AllAcceptedCredentials
Informs the client about all credentials that the server currently recognizes for a given user. This allows the client to remove any credentials that are no longer valid.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
serialize(
// "allAcceptedCredentialIds": ["...", "..."]
// }
```
-{% endcode %}
-## Use Cases
+## Use cases
-### After Credential Deletion
+### After credential deletion
When a user removes a passkey from your application, send an `AllAcceptedCredentials` signal with the remaining credentials so the client can update its list.
-### After Profile Update
+### After profile update
When a user changes their username or display name, send a `CurrentUserDetails` signal so the client displays the correct information in its passkey picker.
-### During Authentication
+### During authentication
If an authentication attempt references a credential that doesn't exist in your database, send an `UnknownCredential` signal to help the client clean up orphaned passkeys.
-## See Also
+## See also
* [WebAuthn Signal API Specification](https://github.com/nicovil/webauthn-signal-api) - W3C specification
* [Credential Record](../../prerequisites/credential-record.md) - Credential storage
diff --git a/pure-php/advanced-behaviours/user-verification.md b/docs/pure-php/advanced-behaviours/user-verification.md
similarity index 85%
rename from pure-php/advanced-behaviours/user-verification.md
rename to docs/pure-php/advanced-behaviours/user-verification.md
index d6a4088..6c91db1 100644
--- a/pure-php/advanced-behaviours/user-verification.md
+++ b/docs/pure-php/advanced-behaviours/user-verification.md
@@ -1,11 +1,10 @@
-# User Verification
+# User verification
You can indicate the user verification requirements during the ceremonies by setting the value in your options.
### Authenticator registration
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
use Webauthn\AuthenticatorSelectionCriteria;
use Webauthn\PublicKeyCredentialCreationOptions;
@@ -18,12 +17,10 @@ $publicKeyCredentialCreationOptions =
)
;
```
-{% endcode %}
-### User Authentication
+### User authentication
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// Public Key Credential Request Options
use Webauthn\PublicKeyCredentialRequestOptions;
@@ -32,4 +29,3 @@ $publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::create(
userVerification: PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_DISCOURAGED
);
```
-{% endcode %}
diff --git a/pure-php/authenticate-your-users.md b/docs/pure-php/authenticate-your-users.md
similarity index 82%
rename from pure-php/authenticate-your-users.md
rename to docs/pure-php/authenticate-your-users.md
index 4207b21..dede0b4 100644
--- a/pure-php/authenticate-your-users.md
+++ b/docs/pure-php/authenticate-your-users.md
@@ -1,8 +1,8 @@
-# Authenticate Your Users
+# Authenticate your users
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.
-## Assertion Request
+## Assertion request
To perform a user authentication using a security device, you need to instantiate a `Webauthn\PublicKeyCredentialRequestOptions` object.
@@ -20,19 +20,18 @@ Optionally, you can customize the following parameters:
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.
-{% hint style="info" %}
-The timeout default value is set to `null`. If you want to set a value, please read the following recommended behavior showed in the specification:
+!!! info
-* 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)
-{% endhint %}
+ The timeout default value is set to `null`. If you want to set a value, please read the following recommended behavior showed in the specification:
-### Allowed Credentials
+ * 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)
+
+### Allowed credentials
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.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
use Webauthn\CredentialRecord;
use Webauthn\PublicKeyCredentialDescriptor;
@@ -50,20 +49,18 @@ $allowedCredentials = array_map(
$registeredAuthenticators
);
```
-{% endcode %}
-{% hint style="info" %}
-For usernameless authentication, please read the [dedicated page](advanced-behaviours/authentication-without-username.md). In this case no Public Key Credential Descriptors should be passed to the options.
-{% endhint %}
+!!! info
+
+ For usernameless authentication, please read the [dedicated page](advanced-behaviours/authentication-without-username.md). In this case no Public Key Credential Descriptors should be passed to the options.
+
+!!! danger
-{% hint style="danger" %}
-To prevent username enumeration, when the authentication process is performed using the username, it is highly recommended to continue the ceremony even if the user entity does not exist. The allowed credential list should be set with random data.
-{% endhint %}
+ To prevent username enumeration, when the authentication process is performed using the username, it is highly recommended to continue the ceremony even if the user entity does not exist. The allowed credential list should be set with random data.
### Example
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
deserialize($data, PublicKeyCredential::class, 'json');
```
-{% endcode %}
-### Response Verification
+### Response verification
Now we have a fully loaded Public Key Credential object, but we need now to make sure that:
@@ -201,8 +189,7 @@ Now we have a fully loaded Public Key Credential object, but we need now to make
The first is easy to perform:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
response instanceof AuthenticatorAssertionResponse) {
//e.g. process here with a redirection to the public key login/MFA page.
}
```
-{% endcode %}
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.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
check(
$credentialRecordRepository->saveCredentialRecord($credentialRecord);
```
-{% endcode %}
If no exception is thrown, the response is valid and you can continue the authentication of the user.
diff --git a/pure-php/authenticator-registration.md b/docs/pure-php/authenticator-registration.md
similarity index 82%
rename from pure-php/authenticator-registration.md
rename to docs/pure-php/authenticator-registration.md
index 0fb65b6..023789c 100644
--- a/pure-php/authenticator-registration.md
+++ b/docs/pure-php/authenticator-registration.md
@@ -1,10 +1,10 @@
-# Register Authenticators
+# Register authenticators
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.
-## Creation Request
+## Creation request
To associate a device to a user, you need to instantiate a `Webauthn\PublicKeyCredentialCreationOptions` object.
@@ -25,8 +25,7 @@ Optionally, you can customize the following parameters:
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".
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
serialize(
);
```
-{% hint style="warning" %}
-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.
-{% endhint %}
+!!! warning
+
+ 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
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
use Webauthn\AuthenticatorSelectionCriteria;
$publicKeyCredentialCreationOptions =
@@ -98,16 +95,14 @@ $publicKeyCredentialCreationOptions =
)
;
```
-{% endcode %}
-### Public Key Credential Parameters
+### Public key credential parameters
The argument `pubKeyCredParams` contains a list of `Webauthn\PublicKeyCredentialParameters` objects that refer to COSE algorithms. The authenticators must use one of the algorithms in this list, respecting the order of preference on this list.
An empty list corresponds to the default algorithms that are `ES256` and `RS256` (in this order). Those two algorithms are required by the specification.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
use Cose\Algorithms;
use Webauthn\PublicKeyCredentialParameters;
@@ -119,13 +114,12 @@ $publicKeyCredentialParametersList = [
PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_ED256), // Less interesting algorithm
];
```
-{% endcode %}
-{% hint style="warning" %}
-Customizing this list may lead to unexpected behavior. Please use with caution.
-{% endhint %}
+!!! warning
-### Authenticator Selection
+ Customizing this list may lead to unexpected behavior. Please use with caution.
+
+### Authenticator selection
Please read detail [on this page](advanced-behaviours/authenticator-selection-criteria.md).
@@ -135,9 +129,9 @@ Please read detail [on this page](advanced-behaviours/attestation-and-metadata-s
### Hints
-{% hint style="info" %}
-**New in v5.3.0**
-{% endhint %}
+!!! info
+
+ **New in v5.3.0**
WebAuthn Level 3 lets the relying party suggest one or several authentication methods through the `hints` parameter. The user agent uses the list (in order) to tailor the prompt — for example, showing the QR code for hybrid transport first.
@@ -147,8 +141,7 @@ The valid values are exposed as constants on `Webauthn\PublicKeyCredentialOption
* `HINT_CLIENT_DEVICE` (`client-device`) — platform authenticator (Touch ID, Windows Hello…)
* `HINT_HYBRID` (`hybrid`) — phone as authenticator over the hybrid transport
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
use Webauthn\PublicKeyCredentialCreationOptions;
use Webauthn\PublicKeyCredentialOptions;
@@ -164,16 +157,14 @@ $publicKeyCredentialCreationOptions =
)
;
```
-{% endcode %}
The same `hints` parameter is available on `PublicKeyCredentialRequestOptions` for authentication ceremonies.
-### Exclude Credentials
+### Exclude credentials
When the user already registered authenticators, you can pass a list of `Webauthn\PublicKeyCredentialDescriptor` objects as argument to avoid registering multiple times the same authenticator.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
use Webauthn\PublicKeyCredentialCreationOptions;
use Webauthn\PublicKeyCredentialDescriptor;
@@ -192,20 +183,18 @@ $publicKeyCredentialCreationOptions =
)
;
```
-{% endcode %}
-{% hint style="danger" %}
-To avoid username enumeration, you should not set any excluded credential when the authenticator registration is performed for anonymous users. If this value is present, it indicates that the username exists and has registered credentials.
+!!! danger
-On the contrary when adding new authenticators to already authenticated users, this value should be set.
-{% endhint %}
+ To avoid username enumeration, you should not set any excluded credential when the authenticator registration is performed for anonymous users. If this value is present, it indicates that the username exists and has registered credentials.
-## Creation Response
+ On the contrary when adding new authenticators to already authenticated users, this value should be set.
+
+## Creation response
What you receive must be a JSON object that looks like the following:
-{% code lineNumbers="true" %}
-```javascript
+```javascript linenums="1"
{
"id":"KVb8CnwDjpgAo[…]op61BTLaa0tczXvz4JrQ23usxVHA8QJZi3L9GZLsAtkcVvWObA",
"type":"public-key",
@@ -216,19 +205,19 @@ What you receive must be a JSON object that looks like the following:
}
}
```
-{% endcode %}
There are two steps to perform with this object:
* [Load the data](input-loading.md)
* [Verify it with the creation options set above](input-validation.md)
-### Data Loading
+### Data loading
+
+Now that all components are set, we can load the data we receive using the [_Serializer_](input-loading.md#the-serializer) (variable `$serializer`).
-Now that all components are set, we can load the data we receive using the [_Serializer_](webauthn-server.md#the-serializer) (variable `$serializer`).
+```php linenums="1" hl_lines="1"
+<?php
-
declare(strict_types=1);
use Webauthn\PublicKeyCredential;
@@ -249,11 +238,11 @@ $publicKeyCredential = $serializer->deserialize(
PublicKeyCredential::class,
'json'
);
-
+```
If no exception is thrown, you can go to the next step: the verification.
-### Response Verification
+### Response verification
Now we have a fully loaded Public Key Credential object, but we need now to make sure that:
@@ -262,8 +251,7 @@ Now we have a fully loaded Public Key Credential object, but we need now to make
The first step is easy to perform:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
response instanceof AuthenticatorAttestationResponse)
//e.g. process here with a redirection to the public key creation page.
}
```
-{% endcode %}
The second step is the verification against
@@ -283,8 +270,7 @@ The second step is the verification against
The Authenticator Attestation Response Validator service (variable `$authenticatorAttestationResponseValidator`) will check everything for you: challenge, origin, attestation statement and much more.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
check(
'my-application.com'
);
```
-{% endcode %}
If no exception is thrown, the response is valid. You can store the Credential Record (`$credentialRecord`).
-{% hint style="info" %}
-The way you store and associate these objects to the user is out of scope of this library.
-{% endhint %}
+!!! info
+
+ The way you store and associate these objects to the user is out of scope of this library.
diff --git a/pure-php/input-loading.md b/docs/pure-php/input-loading.md
similarity index 79%
rename from pure-php/input-loading.md
rename to docs/pure-php/input-loading.md
index cedbaa5..72282c1 100644
--- a/pure-php/input-loading.md
+++ b/docs/pure-php/input-loading.md
@@ -1,14 +1,14 @@
-# Input Loading
+# Input loading
In general, the data you receive is an encoded JSON object. The library provides utilities to convert the string into objects.
To do so, you will need an Attestation Statement Support Manager and a Serializer.
-## Attestation Statement Support Manager
+## Attestation statement support manager
Authenticator Responses may contain an Attestation Statement. This attestation holds 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.
-### Supported Attestation Statement Types
+### Supported attestation statement types
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](../webauthn-in-a-nutshell/attestation-and-metadata-statement.md).
@@ -19,8 +19,7 @@ The following attestation types are supported. Note that you should only use the
* `trusted platform module`: for devices with built-in security chips.
* `apple`: for Apple devices
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
add(NoneAttestationStatementSupport::create());
```
-{% endcode %}
-## The Serializer
+## The serializer
To convert Authenticator Responses from the encoded string to an object, you will need a Serializer. It only needs the Attestation Statement Support Manager created above.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
create();
```
-{% endcode %}
-{% hint style="danger" %}
-Please do not use `json_encode` or `json_decode` to convert this data either way.
-{% endhint %}
+!!! danger
-## Loading Data
+ Please do not use `json_encode` or `json_decode` to convert this data either way.
+
+## Loading data
In general, the data you receive looks like as follows.
-{% code lineNumbers="true" %}
-```json
+```json linenums="1"
{
"id":"KVb8CnwDjpgAo[…]op61BTLaa0tczXvz4JrQ23usxVHA8QJZi3L9GZLsAtkcVvWObA",
"type":"public-key",
@@ -71,14 +66,14 @@ In general, the data you receive looks like as follows.
}
}
```
-{% endcode %}
-{% hint style="info" %}
-Only this type of input is supported. If you receive other forms of data, please contact us.
-{% endhint %}
+!!! info
+
+ Only this type of input is supported. If you receive other forms of data, please contact us.
+
+```php linenums="1" hl_lines="1"
+<?php
-
declare(strict_types=1);
use Webauthn\PublicKeyCredential;
@@ -89,10 +84,10 @@ $publicKeyCredential = $serializer->deserialize(
PublicKeyCredential::class,
'json'
);
-
+```
If the data is correctly loaded, the variable `$publicKeyCredential` will be an instance of `Webauthn\PublicKeyCredential`. An exception is thrown in case of an error.
-{% hint style="danger" %}
-At this stage, the data is not verified.
-{% endhint %}
+!!! danger
+
+ At this stage, the data is not verified.
diff --git a/pure-php/input-validation.md b/docs/pure-php/input-validation.md
similarity index 51%
rename from pure-php/input-validation.md
rename to docs/pure-php/input-validation.md
index e538a43..ee2d734 100644
--- a/pure-php/input-validation.md
+++ b/docs/pure-php/input-validation.md
@@ -1,17 +1,16 @@
-# Input Validation
+# Input validation
The loaded data needs to be verified. The library will perform several actions to make sure the input you received is valid. This verification process is performed by a Ceremony Step Manager (CSM). The WebAuthn specification distinguishes two types of ceremonies [described in this page](../webauthn-in-a-nutshell/ceremonies.md).
-{% hint style="info" %}
-**Changed in v5.3.6:** the `tokenBinding` member of the client data is not validated anymore. It is reserved since WebAuthn Level 3 and its value was never used, so a ceremony is no longer rejected because of it. The raw client data stays available through the `data` property of `CollectedClientData`.
-{% endhint %}
+!!! info
-## Ceremony Step Manager Factory
+ **Changed in v5.3.6:** the `tokenBinding` member of the client data is not validated anymore. It is reserved since WebAuthn Level 3 and its value was never used, so a ceremony is no longer rejected because of it. The raw client data stays available through the `data` property of `CollectedClientData`.
+
+## Ceremony step manager factory
To facilitate the creation of the CSM, a default factory is included. This factory requires no external services to function.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
creationCeremony();
$requestCSM = $csmFactory->requestCeremony();
```
-{% endcode %}
-{% hint style="info" %}
-You can customize its behavior to fit the specific needs of your application by modifying the provided factory. Please refer to the dedicated pages for more information.
+!!! info
+
+ You can customize its behavior to fit the specific needs of your application by modifying the provided factory. Please refer to the dedicated pages for more information.
-* [Counter Checker](advanced-behaviours/authenticator-counter.md)
-* [Attestation Statement Support Manager](advanced-behaviours/attestation-and-metadata-statement.md#attestation-statement-support-manager)
-* [Extension Output Checker Handler](advanced-behaviours/extensions.md)
-* [Algorithm Manager](advanced-behaviours/authenticator-algorithms.md)
-* [Metadata Statement Repository](advanced-behaviours/attestation-and-metadata-statement.md#metadata-statement-repository)
-* [Status Report Repository](advanced-behaviours/attestation-and-metadata-statement.md#status-report-repository)
-* [Certification Chain Validator](advanced-behaviours/attestation-and-metadata-statement.md#certificate-chain-validator)
-{% endhint %}
+ * [Counter Checker](advanced-behaviours/authenticator-counter.md)
+ * [Attestation Statement Support Manager](advanced-behaviours/attestation-and-metadata-statement.md#attestation-statement-support-manager)
+ * [Extension Output Checker Handler](advanced-behaviours/extensions.md)
+ * [Algorithm Manager](advanced-behaviours/authenticator-algorithms.md)
+ * [Metadata Statement Repository](advanced-behaviours/attestation-and-metadata-statement.md#metadata-statement-repository)
+ * [Status Report Repository](advanced-behaviours/attestation-and-metadata-statement.md#status-report-repository)
+ * [Certification Chain Validator](advanced-behaviours/attestation-and-metadata-statement.md#certificate-chain-validator)
These CSM services are meant to be used by Response Validators. In a similar way, there are two types of validators:
* **Authenticator Attestation Response Validator**: used during the creation ceremony
* **Authenticator Assertion Response Validator**: used during the request ceremony
-## Response Validators
+## Response validators
The Authenticator Attestation Response Validator and Authenticator Assertion Response Validator services are directly used when receiving Authenticator Responses in order to [register authenticators](authenticator-registration.md) or [authenticate users](authenticate-your-users.md).
-{% hint style="info" %}
-All null values below correspond to deprecated parameters. They have been removed in 5.0.0
-{% endhint %}
+!!! info
+
+ All null values below correspond to deprecated parameters. They have been removed in 5.0.0
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
check(
// 5. Store the credential
$credentialRepository->saveCredentialSource($credentialSource);
```
-{% endcode %}
-### Authentication Flow
+### Authentication flow
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
check(
// 5. Update credential (counter may have changed)
$credentialRepository->saveCredentialSource($updatedCredential);
```
-{% endcode %}
-## Minimal Setup Example
+## Minimal setup example
Here's a minimal working example with in-memory storage:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
`, are compared verbatim with the origin sent by the client. Entries made of a host only are normalized to `https://`.
-{% endhint %}
+!!! info
-## Top Origin Validation (Cross-Origin iframes)
+ **Changed in v5.3.3:** entries that are not URLs, such as `android:apk-key-hash:`, are compared verbatim with the origin sent by the client. Entries made of a host only are normalized to `https://`.
+
+## Top origin validation (cross-origin iframes)
When the ceremony runs in a cross-origin iframe, the client data contains the origin of the embedding page in a `topOrigin` field.
-{% hint style="danger" %}
-**Changed in v5.3.4:** such a response is rejected unless a top origin validator is configured. Applications that do not use iframes are not affected, as a same-origin ceremony carries no `topOrigin`.
-{% endhint %}
+!!! danger
+
+ **Changed in v5.3.4:** such a response is rejected unless a top origin validator is configured. Applications that do not use iframes are not affected, as a same-origin ceremony carries no `topOrigin`.
Write a service implementing `Webauthn\CeremonyStep\TopOriginValidator`:
-{% code title="src/Security/MyTopOriginValidator.php" lineNumbers="true" %}
-```php
+```php title="src/Security/MyTopOriginValidator.php" linenums="1"
console.log('Registration failure'))
;
```
-{% endcode %}
-### Creation Profile
+### Creation profile
The `default` [creation profile](../configuration-references.md#creation-profiles) is used. You can change it using the dedicated option.
-{% code title="config/packages/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/webauthn.yaml" linenums="1"
webauthn:
controllers:
enabled: true
@@ -137,9 +127,8 @@ webauthn:
…
profile: custom_profile
```
-{% endcode %}
-### Response Handlers
+### Response handlers
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.
@@ -149,12 +138,11 @@ There are 3 types of responses and handlers:
* Success,
* Failure.
-#### Creation Options Handler
+#### Creation options handler
This handler is called during the registration of an authenticator and has to implement the interface `Webauthn\Bundle\Security\Handler\CreationOptionsHandler`.
-{% code title="config/packages/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/webauthn.yaml" linenums="1"
webauthn:
controllers:
enabled: true
@@ -163,14 +151,12 @@ webauthn:
…
options_handler: … # Your handler here
```
-{% endcode %}
-#### Success Handler
+#### Success handler
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`.
-{% code title="config/packages/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/webauthn.yaml" linenums="1"
webauthn:
controllers:
enabled: true
@@ -179,14 +165,12 @@ webauthn:
…
success_handler: … # Your handler here
```
-{% endcode %}
-#### Failure Handler
+#### Failure handler
This handler is called when an error occurred during the process. This handler shall implement the interface `Webauthn\Bundle\Security\Handler\FailureHandler`. The default handler is `Webauthn\Bundle\Service\DefaultFailureHandler`.
-{% code title="config/packages/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/webauthn.yaml" linenums="1"
webauthn:
controllers:
enabled: true
@@ -195,4 +179,3 @@ webauthn:
…
failure_handler: … # Your handler here
```
-{% endcode %}
diff --git a/symfony-bundle/advanced-behaviors/user-verification.md b/docs/symfony-bundle/advanced-behaviors/user-verification.md
similarity index 85%
rename from symfony-bundle/advanced-behaviors/user-verification.md
rename to docs/symfony-bundle/advanced-behaviors/user-verification.md
index a6ae284..00bae3f 100644
--- a/symfony-bundle/advanced-behaviors/user-verification.md
+++ b/docs/symfony-bundle/advanced-behaviors/user-verification.md
@@ -1,9 +1,8 @@
-# User Verification
+# User verification
The easiest way to manage the user verification requirements in your Symfony application is by using the creation and request profiles.
-{% code title="config/packages/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/webauthn.yaml" linenums="1"
webauthn:
…
creation_profiles:
@@ -18,4 +17,3 @@ webauthn:
rp_id: 'example.com'
user_verification: !php/const Webauthn\AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED
```
-{% endcode %}
diff --git a/symfony-bundle/bundle-installation.md b/docs/symfony-bundle/bundle-installation.md
similarity index 62%
rename from symfony-bundle/bundle-installation.md
rename to docs/symfony-bundle/bundle-installation.md
index 07cd661..6f2edce 100644
--- a/symfony-bundle/bundle-installation.md
+++ b/docs/symfony-bundle/bundle-installation.md
@@ -1,12 +1,12 @@
-# Bundle Installation
+# Bundle installation
This framework provides a Symfony bundle that will help you to use the components within your Symfony application.
-{% hint style="info" %}
-Implementations for other frameworks and web applications exist.
+!!! info
-As an example, if you use Laravel, you may be interested in [this project: https://github.com/asbiin/laravel-webauthn](https://github.com/asbiin/laravel-webauthn)
-{% endhint %}
+ Implementations for other frameworks and web applications exist.
+
+ As an example, if you use Laravel, you may be interested in [this project: https://github.com/asbiin/laravel-webauthn](https://github.com/asbiin/laravel-webauthn)
## Installation
@@ -16,16 +16,15 @@ As an example, if you use Laravel, you may be interested in [this project: https
composer require web-auth/webauthn-symfony-bundle
```
-{% hint style="info" %}
-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.`
-{% endhint %}
+!!! info
+
+ 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.`
### Without Symfony Flex
If you don't use Symfony Flex, you must register the bundle and route manually.
-{% code title="config/bundles.php" lineNumbers="true" %}
-```php
+```php title="config/bundles.php" linenums="1"
['all' => true],
];
```
-{% endcode %}
-{% code title="config/routes/webauthn.php" lineNumbers="true" %}
-```php
+```php title="config/routes/webauthn.php" linenums="1"
import('.', 'webauthn');
};
```
-{% endcode %}
## Repositories
diff --git a/symfony-bundle/configuration-references.md b/docs/symfony-bundle/configuration-references.md
similarity index 72%
rename from symfony-bundle/configuration-references.md
rename to docs/symfony-bundle/configuration-references.md
index 11b72f3..4e2e78b 100644
--- a/symfony-bundle/configuration-references.md
+++ b/docs/symfony-bundle/configuration-references.md
@@ -1,4 +1,4 @@
-# Configuration References
+# Configuration references
## Configuration
@@ -8,8 +8,7 @@ You may also need to adjust other parameters.
If you don't use Flex, hereafter an example of configuration file:
-{% code title="config/packages/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/webauthn.yaml" linenums="1"
webauthn:
# logger: null # PSR-3 compatible logging service
credential_repository: 'Webauthn\Bundle\Repository\DummyPublicKeyCredentialSourceRepository' # CREATE YOUR REPOSITORY AND CHANGE THIS!
@@ -88,32 +87,30 @@ webauthn:
# status_report_repository: 'App\Repository\StatusReportRepository'
# certificate_chain_checker: 'App\Security\CertificateChainChecker'
```
-{% endcode %}
-### Creation Profiles
+### Creation profiles
-{% hint style="success" %}
-If you don't create the `creation_profiles` section, a `default` profile is set.
-{% endhint %}
+!!! success
-#### Relying Party (rp)
+ If you don't create the `creation_profiles` section, a `default` profile is set.
+
+#### Relying party (rp)
The Relying Party corresponds to your application. Please refer [to this page](../prerequisites/the-relying-party.md) for more information.
-{% hint style="warning" %}
-The parameter `id` is optional but highly recommended.
-{% endhint %}
+!!! warning
+
+ The parameter `id` is optional but highly recommended.
-{% hint style="info" %}
-The parameters `name` and `icon` are deprecated (since 5.3.0 and 5.1.0 respectively) and are removed in 6.0.0. Since 5.3.7, a profile without `rp.name` produces options where the `name` member defaults to the Relying Party ID, and no deprecation is triggered.
-{% endhint %}
+!!! info
-#### Challenge Length
+ The parameters `name` and `icon` are deprecated (since 5.3.0 and 5.1.0 respectively) and are removed in 6.0.0. Since 5.3.7, a profile without `rp.name` produces options where the `name` member defaults to the Relying Party ID, and no deprecation is triggered.
+
+#### Challenge length
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:
-{% code title="app/config/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="app/config/webauthn.yaml" linenums="1"
webauthn:
creation_profiles:
acme:
@@ -121,14 +118,12 @@ webauthn:
name: 'ACME WebAuthn Server'
challenge_length: 16
```
-{% endcode %}
#### Timeout
The default timeout is set to 60 seconds (60 000 milliseconds). You can change this value as follows:
-{% code title="app/config/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="app/config/webauthn.yaml" linenums="1"
webauthn:
creation_profiles:
acme:
@@ -136,23 +131,21 @@ webauthn:
name: 'ACME WebAuthn Server'
timeout: 30000
```
-{% endcode %}
-{% hint style="info" %}
-For v4.0+, the timeout is set to `null`. The values recommended by the specification are as follows:
+!!! info
+
+ For v4.0+, the timeout is set to `null`. The values recommended by the specification are as follows:
-* 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)
+ * 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.
-{% endhint %}
+ These behaviors are not necessarily followed by the web browsers.
-#### Authenticator Selection Criteria
+#### Authenticator selection criteria
This set of options allows you to select authenticators depending on their capabilities. The values are described in [the advanced concepts](../pure-php/advanced-behaviours/authenticator-selection-criteria.md) of the protocol.
-{% code title="app/config/webauthn.yaml" lineNumbers="true" fullWidth="false" %}
-```yaml
+```yaml title="app/config/webauthn.yaml" linenums="1"
webauthn:
creation_profiles:
acme:
@@ -163,22 +156,20 @@ webauthn:
require_resident_key: true
user_verification: !php/const Webauthn\AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED
```
-{% endcode %}
-#### Public Key Credential Parameters
+#### Public key credential parameters
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.
-{% hint style="info" %}
-The order is important. Preferred algorithms go first.
-{% endhint %}
+!!! info
-{% hint style="warning" %}
-It is not recommended to change the default list unless you exactly know what you are doing.
-{% endhint %}
+ The order is important. Preferred algorithms go first.
-{% code title="app/config/webauthn.yaml" lineNumbers="true" %}
-```yaml
+!!! warning
+
+ It is not recommended to change the default list unless you exactly know what you are doing.
+
+```yaml title="app/config/webauthn.yaml" linenums="1"
webauthn:
creation_profiles:
acme:
@@ -188,40 +179,36 @@ webauthn:
- !php/const Cose\Algorithms::COSE_ALGORITHM_ES256
- !php/const Cose\Algorithms::COSE_ALGORITHM_RS256
```
-{% endcode %}
-#### Signature Verification Algorithms
+#### Signature verification algorithms
The parameters above list the algorithms offered to the authenticator during the registration. The signatures themselves are verified by `webauthn.cose.algorithm.manager`, which the bundle fills with one service per supported algorithm. Any service implementing `Cose\Algorithm\Algorithm` is collected automatically, so declaring one is enough to add an algorithm to that manager.
-{% hint style="danger" %}
-**Backward compatibility break in v5.3.8:** the bundle no longer registers `RS1` (RSASSA-PKCS1-v1\_5 with SHA-1). SHA-1 is no longer acceptable for digital signatures, and building the algorithm emits an `E_USER_WARNING` that the Symfony error handler turns into an exception, so every ceremony answered with a 500 in the dev environment.
+!!! danger
-`RS1` was never offered at the registration, as `public_key_credential_parameters` is empty by default. The applications that added `Cose\Algorithms::COSE_ALGORITHM_RS1` to that list are the only ones affected: without the service, the assertions signed by the credentials they registered are rejected.
-{% endhint %}
+ **Backward compatibility break in v5.3.8:** the bundle no longer registers `RS1` (RSASSA-PKCS1-v1\_5 with SHA-1). SHA-1 is no longer acceptable for digital signatures, and building the algorithm emits an `E_USER_WARNING` that the Symfony error handler turns into an exception, so every ceremony answered with a 500 in the dev environment.
+
+ `RS1` was never offered at the registration, as `public_key_credential_parameters` is empty by default. The applications that added `Cose\Algorithms::COSE_ALGORITHM_RS1` to that list are the only ones affected: without the service, the assertions signed by the credentials they registered are rejected.
If you are in that situation, declare the algorithm yourself. Autoconfiguration adds it back to the manager.
-{% code title="config/services.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/services.yaml" linenums="1"
services:
webauthn.cose.algorithm.RS1:
class: Cose\Algorithm\Signature\RSA\RS1
arguments:
- true # Acknowledge that the algorithm is insecure
```
-{% endcode %}
-#### Attestation Conveyance
+#### Attestation conveyance
If you need the [attestation of the authenticator](../webauthn-in-a-nutshell/attestation-and-metadata-statement.md), you can specify the preference regarding attestation conveyance during credential generation.
-{% hint style="warning" %}
-The use of Attestation Statements is generally not recommended unless you REALLY need this information.
-{% endhint %}
+!!! warning
+
+ The use of Attestation Statements is generally not recommended unless you REALLY need this information.
-{% code title="app/config/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="app/config/webauthn.yaml" linenums="1"
webauthn:
creation_profiles:
acme:
@@ -229,18 +216,16 @@ webauthn:
name: 'ACME WebAuthn Server'
attestation_conveyance: !php/const Webauthn\PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT
```
-{% endcode %}
#### Hints
-{% hint style="info" %}
-**New in v5.3.0:** Support for WebAuthn hints to guide user authentication choices.
-{% endhint %}
+!!! info
+
+ **New in v5.3.0:** Support for WebAuthn hints to guide user authentication choices.
Hints provide guidance to the user agent about how the user might authenticate. This can help improve the user experience by suggesting the most appropriate authentication method.
-{% code title="app/config/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="app/config/webauthn.yaml" linenums="1"
webauthn:
creation_profiles:
acme:
@@ -257,23 +242,21 @@ webauthn:
- 'security-key'
- 'client-device'
```
-{% endcode %}
Available hint values:
- `security-key`: External security key (like YubiKey)
- `client-device`: Platform authenticator (like Touch ID, Windows Hello)
- `hybrid`: Hybrid transport (like phone as authenticator)
-#### Hide Existing Credentials
+#### Hide existing credentials
+
+!!! info
-{% hint style="info" %}
-**New in v5.3.0:** Exposed in bundle configuration.
-{% endhint %}
+ **New in v5.3.0:** Exposed in bundle configuration.
When set to `true`, the server will automatically add the user's existing credentials to the `excludeCredentials` list in the creation options. This prevents the user from re-registering the same authenticator.
-{% code title="app/config/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="app/config/webauthn.yaml" linenums="1"
webauthn:
creation_profiles:
acme:
@@ -281,18 +264,16 @@ webauthn:
name: 'ACME WebAuthn Server'
hide_existing_credentials: true
```
-{% endcode %}
-#### Conditional Create
+#### Conditional create
-{% hint style="info" %}
-**New in v5.3.0**
-{% endhint %}
+!!! info
+
+ **New in v5.3.0**
Enable Conditional Create (auto-register) for a profile. When `true`, user presence can be `false` during registration, allowing silent credential creation after the user has authenticated via another method (e.g., password).
-{% code title="app/config/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="app/config/webauthn.yaml" linenums="1"
webauthn:
creation_profiles:
passkey_upgrade:
@@ -302,20 +283,18 @@ webauthn:
authenticator_selection_criteria:
require_resident_key: true
```
-{% endcode %}
See [Conditional Create](../pure-php/advanced-behaviours/conditional-create.md) for detailed usage.
-#### Client Override Policy
+#### Client override policy
+
+!!! info
-{% hint style="info" %}
-**New in v5.3.0**
-{% endhint %}
+ **New in v5.3.0**
Control which WebAuthn options can be overridden by client request parameters. See [Client Override Policy](advanced-behaviors/client-override-policy.md) for detailed configuration.
-{% code title="app/config/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="app/config/webauthn.yaml" linenums="1"
webauthn:
creation_profiles:
acme:
@@ -328,18 +307,16 @@ webauthn:
authenticator_attachment:
enabled: false # Prevent client override
```
-{% endcode %}
#### Extensions
You can set as many extensions as you want in the profile. Please also [refer to this page](../webauthn-in-a-nutshell/extensions.md) for more information.
-{% hint style="info" %}
-The example below is totally fictive. Some extensions are [defined in the specification](https://www.w3.org/TR/webauthn/#sctn-defined-extensions) but the support depends on the authenticators, on the browsers and on the relying parties (your applications).
-{% endhint %}
+!!! info
-{% code title="app/config/webauthn.yaml" lineNumbers="true" %}
-```yaml
+ The example below is totally fictive. Some extensions are [defined in the specification](https://www.w3.org/TR/webauthn/#sctn-defined-extensions) but the support depends on the authenticators, on the browsers and on the relying parties (your applications).
+
+```yaml title="app/config/webauthn.yaml" linenums="1"
webauthn:
creation_profiles:
acme:
@@ -349,49 +326,42 @@ webauthn:
loc: true
txAuthSimple: 'Please add your new authenticator'
```
-{% endcode %}
-### Request Profiles
+### Request profiles
+
+!!! success
-{% hint style="success" %}
-If you don't create the `request_profiles` section, a `default` profile is set.
-{% endhint %}
+ If you don't create the `request_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).
-{% code title="app/config/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="app/config/webauthn.yaml" linenums="1"
webauthn:
request_profiles:
acme:
rp_id: 'example.com'
```
-{% endcode %}
Please note that all parameters are optional. The following configuration is perfectly valid. However, and as mentioned above, the parameter `id` is highly recommended.
-{% code title="app/config/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="app/config/webauthn.yaml" linenums="1"
webauthn:
request_profiles:
acme: ~
```
-{% endcode %}
-### Passkey Endpoints
+### Passkey endpoints
+
+!!! info
-{% hint style="info" %}
-**New in v5.3.0**
-{% endhint %}
+ **New in v5.3.0**
Enable the `.well-known/passkey-endpoints` discovery endpoint. See [Passkey Endpoints](advanced-behaviors/passkey-endpoints.md) for detailed configuration.
-{% code title="app/config/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="app/config/webauthn.yaml" linenums="1"
webauthn:
passkey_endpoints:
enabled: true
enroll: 'https://example.com/passkeys/register'
manage: 'https://example.com/passkeys/manage'
```
-{% endcode %}
diff --git a/symfony-bundle/credential-record-repository.md b/docs/symfony-bundle/credential-record-repository.md
similarity index 56%
rename from symfony-bundle/credential-record-repository.md
rename to docs/symfony-bundle/credential-record-repository.md
index 0ef5293..a9df6fb 100644
--- a/symfony-bundle/credential-record-repository.md
+++ b/docs/symfony-bundle/credential-record-repository.md
@@ -2,50 +2,49 @@
description: Where the public keys and details are stored
---
-# Credential Record Repository
+# Credential record repository
The Credential Record can be stored the way you want. As the `Webauthn\CredentialRecord` class can be converted into JSON, it could be stored in a filesystem.
It is up to you to create a credential record repository. This service shall implement `Webauthn\Bundle\Repository\CredentialRecordRepositoryInterface`.
-{% hint style="warning" %}
-**Renamed in v5.3.0:** `PublicKeyCredentialSource` has been renamed to `CredentialRecord` and `PublicKeyCredentialSourceRepositoryInterface` to `CredentialRecordRepositoryInterface`. The old names are deprecated and will be removed in 6.0.
-{% endhint %}
+!!! warning
-{% hint style="info" %}
-**Fixed in v5.3.7:** the bundle aliases the deprecated `PublicKeyCredentialSourceRepositoryInterface` to your repository only when that repository actually implements it. Symfony refuses an interface alias whose target does not implement the interface, so a repository following the deprecation and implementing `CredentialRecordRepositoryInterface` alone made the container invalid. Applications whose repository still implements the legacy interface keep the alias, and code type-hinting it keeps working.
-{% endhint %}
+ **Renamed in v5.3.0:** `PublicKeyCredentialSource` has been renamed to `CredentialRecord` and `PublicKeyCredentialSourceRepositoryInterface` to `CredentialRecordRepositoryInterface`. The old names are deprecated and will be removed in 6.0.
-{% hint style="warning" %}
-Doctrine users: the field type for `transports` and `other_ui` changed from `array` to `json` (`array` is now deprecated) between the bundle v4.x and 5.0.
+!!! info
-Please make sure to reflect the changes to your data model.
+ **Fixed in v5.3.7:** the bundle aliases the deprecated `PublicKeyCredentialSourceRepositoryInterface` to your repository only when that repository actually implements it. Symfony refuses an interface alias whose target does not implement the interface, so a repository following the deprecation and implementing `CredentialRecordRepositoryInterface` alone made the container invalid. Applications whose repository still implements the legacy interface keep the alias, and code type-hinting it keeps working.
-Here is an example for Postgres:
+!!! warning
-```sql
-ALTER TABLE [/*TABLE NAME HERE*/] ALTER transports TYPE JSON USING transports::JSON
-ALTER TABLE [/*TABLE NAME HERE*/] ALTER other_ui TYPE JSON USING other_ui::JSON
+ Doctrine users: the field type for `transports` and `other_ui` changed from `array` to `json` (`array` is now deprecated) between the bundle v4.x and 5.0.
-```
-{% endhint %}
+ Please make sure to reflect the changes to your data model.
+
+ Here is an example for Postgres:
+
+ ```sql
+ ALTER TABLE [/*TABLE NAME HERE*/] ALTER transports TYPE JSON USING transports::JSON
+ ALTER TABLE [/*TABLE NAME HERE*/] ALTER other_ui TYPE JSON USING other_ui::JSON
-## Registration Capability
+ ```
+
+## Registration capability
By default, the User Entity Repository is not able to register any user account. You can add this behavior by implementing the interface `Webauthn\Bundle\Repository\CanRegisterUserEntity`.
-## Doctrine Repository
+## Doctrine repository
In general, Symfony applications use Doctrine, which is why the bundle provides a way to use Doctrine as storage system.
-### The Doctrine Entity
+### The Doctrine entity
Here is an example of an entity.
This is the most simple example. Feel free to add custom fields that fit your needs e.g. `created_at` or `is_revoked`.
-{% code title="App/Entity/WebauthnCredential.php" lineNumbers="true" %}
-```php
+```php title="App/Entity/WebauthnCredential.php" linenums="1"
<?php
+!!! warning
+
+ We must override the method `saveCredentialRecord` because we may receive `Webauthn\CredentialRecord` objects instead of `App\Entity\WebauthnCredential`.
+
+```php title="App/Repository/WebauthnCredentialRepository.php" linenums="1" hl_lines="7"
+use App\Entity\WebauthnCredential;
-use Doctrine\Persistence\ManagerRegistry;
+use App\Entity\WebauthnCredential;
+use Doctrine\Persistence\ManagerRegistry;
use Webauthn\Bundle\Repository\DoctrineCredentialSourceRepository;
use Webauthn\CredentialRecord;
@@ -152,18 +151,15 @@ final class WebauthnCredentialRepository extends DoctrineCredentialSourceReposit
parent::saveCredentialSource($credentialRecord);
}
}
-
-
+```
This repository should be declared as a Symfony service.
-{% hint style="info" %}
-With Symfony autowiring and autoconfiguration, this is usually done automatically
-{% endhint %}
+!!! info
+
+ With Symfony autowiring and autoconfiguration, this is usually done automatically
-{% code title="config/services.yaml" %}
-```yaml
+```yaml title="config/services.yaml"
services:
App\Repository\WebauthnCredentialRepository: ~
```
-{% endcode %}
diff --git a/symfony-bundle/firewall.md b/docs/symfony-bundle/firewall.md
similarity index 89%
rename from symfony-bundle/firewall.md
rename to docs/symfony-bundle/firewall.md
index 3e72b7c..2825d88 100644
--- a/symfony-bundle/firewall.md
+++ b/docs/symfony-bundle/firewall.md
@@ -4,14 +4,13 @@ description: How to register and authenticate my users?
# Firewall
-## Security Bundle
+## Security bundle
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 [the official documentation](https://symfony.com/doc/current/security.html).
At the end of the installation and configuration, you should have a `config/packages/security.yaml` file that looks like the following:
-{% code title="config/packages/security.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/security.yaml" linenums="1"
security:
providers:
default:
@@ -22,7 +21,6 @@ security:
path: 'logout'
...
```
-{% endcode %}
## Controller
@@ -88,7 +86,7 @@ Below is an example of a login form using the [Stimulus Controller](../symfony-u
{% endblock %}
```
-## Login Authenticator
+## Login authenticator
Next, we need a Symfony Authenticator to handle login form submissions. With WebAuthn, we use a dedicated Passport that shall contain a specific Badge.
@@ -145,12 +143,11 @@ final class LoginAuthenticator extends WebauthnAuthenticator
}
```
-## Security Configuration
+## Security configuration
To enable the user authentication, you just have to declare the Symfony Authenticator in the appropriate firewall (here `main`).
-{% code title="config/packages/security.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/security.yaml" linenums="1"
security:
providers:
default:
@@ -164,16 +161,14 @@ security:
- { path: ^/login, roles: PUBLIC_ACCESS, requires_channel: 'https'}
- { path: ^/logout, roles: PUBLIC_ACCESS}
```
-{% endcode %}
-{% hint style="info" %}
-**Multiple User Providers (v5.2.1+):** The bundle now supports multiple user providers. If your application uses multiple providers, you can configure them in the security configuration and the WebauthnBadge will work correctly with all of them.
-{% endhint %}
+!!! info
+
+ **Multiple User Providers (v5.2.1+):** The bundle now supports multiple user providers. If your application uses multiple providers, you can configure them in the security configuration and the WebauthnBadge will work correctly with all of them.
Also, you need to define the credential options endpoint.
-{% code title="config/packages/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/webauthn.yaml" linenums="1"
webauthn:
controllers:
enabled: true
@@ -181,13 +176,12 @@ webauthn:
login:
options_path: '/login/webauthn/options'
```
-{% endcode %}
-## User Registration
+## User registration
TO BE WRITTEN
-## Authentication Attributes
+## Authentication attributes
The security token returned by the firewall sets some attributes depending on the assertion and the capabilities of the authenticator. The attributes are:
@@ -196,10 +190,8 @@ The security token returned by the firewall sets some attributes depending on th
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.
-{% code title="config/packages/security.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/security.yaml" linenums="1"
security:
access_control:
- { path: ^/admin, roles: [ROLE_ADMIN, IS_USER_VERIFIED]}
```
-{% endcode %}
diff --git a/symfony-bundle/user-entity-repository.md b/docs/symfony-bundle/user-entity-repository.md
similarity index 92%
rename from symfony-bundle/user-entity-repository.md
rename to docs/symfony-bundle/user-entity-repository.md
index 57cdd1f..d14243b 100644
--- a/symfony-bundle/user-entity-repository.md
+++ b/docs/symfony-bundle/user-entity-repository.md
@@ -1,6 +1,6 @@
-# User Entity Repository
+# User entity repository
-## User Entity
+## User entity
Your application certainly has a user repository. Good news: there is no need to modify it!
@@ -10,8 +10,7 @@ This repository should be declared as a Symfony service and shall implement `Web
Here is an example where the application User Repository is injected. This repository uses Doctrine and provides `findOneBy*` methods.
-{% code title="src/Repository/WebauthnUserEntityRepository.php" lineNumbers="true" %}
-```php
+```php title="src/Repository/WebauthnUserEntityRepository.php" linenums="1"
{% endblock %}
```
-{% endcode %}
-## Adding Additional Authenticators
+## Adding additional authenticators
Allow authenticated users to register additional authenticators:
-{% code title="templates/security/add_authenticator.html.twig" lineNumbers="true" %}
-```twig
+```twig title="templates/security/add_authenticator.html.twig" linenums="1"
{% extends 'base.html.twig' %}
{% block body %}
@@ -135,18 +130,16 @@ Allow authenticated users to register additional authenticators:
{% endblock %}
```
-{% endcode %}
-{% hint style="info" %}
-The controller configuration for `add_device` should use `CurrentUserEntityGuesser` to automatically get the authenticated user. See the [User Registration](user-registration.md#configuration-for-existing-users) page for configuration details.
-{% endhint %}
+!!! info
-## Removing Authenticators
+ The controller configuration for `add_device` should use `CurrentUserEntityGuesser` to automatically get the authenticated user. See the [User Registration](user-registration.md#configuration-for-existing-users) page for configuration details.
+
+## Removing authenticators
Allow users to remove authenticators they no longer use:
-{% code title="src/Controller/SecurityController.php" lineNumbers="true" %}
-```php
+```php title="src/Controller/SecurityController.php" linenums="1"
#[Route('/security/authenticators/{id}/remove', name: 'app_remove_authenticator')]
#[IsGranted('ROLE_USER')]
public function removeAuthenticator(
@@ -177,23 +170,21 @@ public function removeAuthenticator(
return $this->redirectToRoute('app_list_authenticators');
}
```
-{% endcode %}
-{% hint style="warning" %}
-**Important Security Considerations:**
+!!! warning
+
+ **Important Security Considerations:**
-* Always verify the authenticator belongs to the current user before removal
-* Prevent users from removing their last authenticator (would lock them out)
-* Consider requiring re-authentication before removal for sensitive accounts
-* Log authenticator additions and removals for security auditing
-{% endhint %}
+ * Always verify the authenticator belongs to the current user before removal
+ * Prevent users from removing their last authenticator (would lock them out)
+ * Consider requiring re-authentication before removal for sensitive accounts
+ * Log authenticator additions and removals for security auditing
-## Naming Authenticators
+## Naming authenticators
Allow users to give friendly names to their authenticators for easier management:
-{% code title="src/Entity/WebauthnCredential.php" lineNumbers="true" %}
-```php
+```php title="src/Entity/WebauthnCredential.php" linenums="1"
use Doctrine\ORM\Mapping as ORM;
use Webauthn\CredentialRecord;
@@ -231,12 +222,10 @@ class WebauthnCredential extends CredentialRecord
// ... other methods
}
```
-{% endcode %}
Then allow users to set names during or after registration:
-{% code title="templates/security/add_authenticator.html.twig" lineNumbers="true" %}
-```twig
+```twig title="templates/security/add_authenticator.html.twig" linenums="1"
```
-{% endcode %}
-## Best Practices
+## Best practices
-### Encourage Backup Authenticators
+### Encourage backup authenticators
Prompt users to register a backup authenticator after their first registration:
-{% code lineNumbers="true" %}
-```twig
+```twig linenums="1"
{% if credential_count == 1 %}
Add a backup authenticator!
@@ -270,9 +257,8 @@ Prompt users to register a backup authenticator after their first registration:
{% endif %}
```
-{% endcode %}
-### Authenticator Metadata
+### Authenticator metadata
Display useful information about each authenticator:
@@ -281,14 +267,14 @@ Display useful information about each authenticator:
* **Registration date**: Track when each authenticator was added
* **AAGUID**: Identify the authenticator model (if available)
-### Security Recommendations
+### Security recommendations
* **Minimum authenticators**: Consider requiring at least 2 authenticators for privileged accounts
* **Maximum authenticators**: Limit to prevent abuse (e.g., 10 per user)
* **Inactive authenticators**: Automatically remove authenticators not used for a long period
* **Notification**: Email users when authenticators are added or removed
-## See Also
+## See also
* [User Registration](user-registration.md) - Register the first authenticator
* [User Authentication](user-authentication.md) - Authenticate with any registered authenticator
diff --git a/symfony-ux/installation.md b/docs/symfony-ux/installation.md
similarity index 80%
rename from symfony-ux/installation.md
rename to docs/symfony-ux/installation.md
index 184265b..43a42e8 100644
--- a/symfony-ux/installation.md
+++ b/docs/symfony-ux/installation.md
@@ -13,9 +13,9 @@ Before installing the Stimulus Controller, you need:
Since v5.3.0 the Stimulus controllers are published on npm as [`@web-auth/webauthn-stimulus`](https://www.npmjs.com/package/@web-auth/webauthn-stimulus). The npm package is the canonical and only maintained source going forward — the legacy `web-auth/webauthn-stimulus` Composer package is deprecated and will be removed in 6.0.0.
-{% hint style="danger" %}
-**Do not declare this package in `assets/controllers.json`.** That file is reserved for Symfony **PHP** UX packages. `Symfony\UX\StimulusBundle` resolves every entry against an installed Composer package, so adding `@web-auth/webauthn-stimulus` there throws `Could not find package "web-auth/webauthn-stimulus" referred to from controllers.json` as soon as the Composer wrapper is gone (the very thing we want). Register the controllers from your JavaScript code instead — see below.
-{% endhint %}
+!!! danger
+
+ **Do not declare this package in `assets/controllers.json`.** That file is reserved for Symfony **PHP** UX packages. `Symfony\UX\StimulusBundle` resolves every entry against an installed Composer package, so adding `@web-auth/webauthn-stimulus` there throws `Could not find package "web-auth/webauthn-stimulus" referred to from controllers.json` as soon as the Composer wrapper is gone (the very thing we want). Register the controllers from your JavaScript code instead — see below.
### 1. Pin the package in your importmap
@@ -37,8 +37,7 @@ The package depends on `@hotwired/stimulus` and `@simplewebauthn/browser`, which
Import the controllers from the package entry point and register them on your Stimulus `Application`. Use the same `--`-separated identifiers that `stimulus_controller('@web-auth/webauthn-stimulus/...')` produces, so the existing Twig helpers keep working unchanged:
-{% code title="assets/bootstrap.js" lineNumbers="true" %}
-```javascript
+```javascript title="assets/bootstrap.js" linenums="1"
import { Application } from '@hotwired/stimulus';
import {
AuthenticationController,
@@ -55,19 +54,18 @@ app.register('web-auth--webauthn-stimulus--registration', RegistrationController
// Legacy combined controller — only register if you still rely on it
app.register('web-auth--webauthn-stimulus', WebauthnController);
```
-{% endcode %}
-{% hint style="info" %}
-**Why these identifiers?** `stimulus_controller('@vendor/pkg/ctrl')` simply transforms the name to a flat string by stripping the leading `@` and turning `/` into `--`. So `stimulus_controller('@web-auth/webauthn-stimulus/authentication')` renders `data-controller="web-auth--webauthn-stimulus--authentication"` — the same identifier you registered in JS. Existing Twig templates that use the `@`-prefixed form keep working without changes.
+!!! info
+
+ **Why these identifiers?** `stimulus_controller('@vendor/pkg/ctrl')` simply transforms the name to a flat string by stripping the leading `@` and turning `/` into `--`. So `stimulus_controller('@web-auth/webauthn-stimulus/authentication')` renders `data-controller="web-auth--webauthn-stimulus--authentication"` — the same identifier you registered in JS. Existing Twig templates that use the `@`-prefixed form keep working without changes.
-The exact location of your Stimulus bootstrap file depends on your project layout. With the default Symfony AssetMapper recipe it is `assets/bootstrap.js` or `assets/app.js`. Anywhere your `Application.start()` runs is fine.
-{% endhint %}
+ The exact location of your Stimulus bootstrap file depends on your project layout. With the default Symfony AssetMapper recipe it is `assets/bootstrap.js` or `assets/app.js`. Anywhere your `Application.start()` runs is fine.
### Deprecated: install via Composer (`web-auth/webauthn-stimulus`)
-{% hint style="danger" %}
-This installation path is kept for backward compatibility only. New projects should use the npm package above.
-{% endhint %}
+!!! danger
+
+ This installation path is kept for backward compatibility only. New projects should use the npm package above.
```bash
composer require web-auth/webauthn-stimulus
@@ -75,7 +73,7 @@ composer require web-auth/webauthn-stimulus
This command installs the PHP wrapper, which used to register the Stimulus controllers via Symfony Flex and configure AssetMapper to import them from a vendored copy. As of v5.3.0 the same JavaScript ships on npm and the PHP wrapper no longer brings any added value — it is scheduled for removal in 6.0.0.
-## What's Included
+## What's included
The package provides Stimulus controllers that handle:
@@ -88,17 +86,17 @@ The package provides Stimulus controllers that handle:
* **PRF extension** — built-in support for the Pseudo-Random Function extension
* **TypeScript declarations** (v5.3.3+): the package ships `.d.ts` files alongside the controllers and declares them through the `types` entry of its `package.json`, so editors and `tsc` know the values, targets, actions and events of each controller without any extra `@types` package
-{% hint style="info" %}
-**Three controllers ship in the package:**
+!!! info
+
+ **Three controllers ship in the package:**
-* `WebauthnController` — combined controller (legacy, still supported)
-* `AuthenticationController` — dedicated authentication controller
-* `RegistrationController` — dedicated registration controller
+ * `WebauthnController` — combined controller (legacy, still supported)
+ * `AuthenticationController` — dedicated authentication controller
+ * `RegistrationController` — dedicated registration controller
-The dedicated controllers offer better separation of concerns, enhanced conditional UI support, and improved error handling. The combined controller remains available for backward compatibility.
-{% endhint %}
+ The dedicated controllers offer better separation of concerns, enhanced conditional UI support, and improved error handling. The combined controller remains available for backward compatibility.
-## Basic Usage
+## Basic usage
Once the controllers are registered, you can use them in your Twig templates with the standard Symfony UX helpers:
@@ -108,10 +106,9 @@ Once the controllers are registered, you can use them in your Twig templates wit
The first argument is the package-prefixed name — it will resolve to the `--`-separated identifier you registered in step 2.
-### Registration Form Example
+### Registration form example
-{% code title="templates/registration/register.html.twig" lineNumbers="true" %}
-```twig
+```twig title="templates/registration/register.html.twig" linenums="1"
```
-{% endcode %}
-### Authentication Form Example
+### Authentication form example
-{% code title="templates/security/login.html.twig" lineNumbers="true" %}
-```twig
+```twig title="templates/security/login.html.twig" linenums="1"
```
-{% endcode %}
-## Controller Options
+## Controller options
The dedicated controllers accept the following options. See the [User Authentication](user-authentication.md) and [User Registration](user-registration.md) pages for the full list of values, targets, actions and events.
-### Registration Options
+### Registration options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
@@ -192,7 +186,7 @@ The dedicated controllers accept the following options. See the [User Authentica
| `successRedirectUri` | string | – | URL to redirect after successful registration |
| `autoRegister` | boolean | `false` | Auto-start registration on page load |
-### Authentication Options
+### Authentication options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
@@ -225,7 +219,7 @@ If you see console errors:
3. Verify WebAuthn routes are properly configured in `config/routes/webauthn.yaml`.
4. Ensure the controller is loaded by checking the browser's network tab.
-## HTTPS Requirement
+## HTTPS requirement
WebAuthn **requires HTTPS** to function. For development, use the Symfony CLI for automatic HTTPS:
@@ -236,7 +230,7 @@ symfony serve
For production, ensure HTTPS is properly configured on your web server.
-## Next Steps
+## Next steps
Now that the Stimulus Controller is installed, proceed to:
@@ -244,7 +238,7 @@ Now that the Stimulus Controller is installed, proceed to:
2. **[User Authentication](user-authentication.md)** — implement login flows
3. **[Additional Authenticators](additional-authenticators.md)** — allow users to register backup devices
-## See Also
+## See also
* [Symfony UX Documentation](https://symfony.com/doc/current/frontend/ux.html) — official UX guide
* [Stimulus Documentation](https://stimulus.hotwired.dev/) — Stimulus framework reference
diff --git a/symfony-ux/user-authentication.md b/docs/symfony-ux/user-authentication.md
similarity index 90%
rename from symfony-ux/user-authentication.md
rename to docs/symfony-ux/user-authentication.md
index 624f30c..5654909 100644
--- a/symfony-ux/user-authentication.md
+++ b/docs/symfony-ux/user-authentication.md
@@ -1,4 +1,4 @@
-# User Authentication
+# User authentication
With the version 5.2 of the bundle, the login process is very similar to the username/password login.
@@ -54,7 +54,7 @@ The `requestResultField` parameter corresponds to the selector to the hidden fie
The behavior after the login is managed by your Symfony Security Authenticator.
-### Browser Autofill
+### Browser autofill
When authenticators are available on the device and the browser is aware of them, you can simplify the way users will sign in. When this feature is enabled, the user will see the list of available authenticators when focusing on the username field. Selecting an account in the list will automatically perform the authentication actions. There is a simple option to enable this feature:
@@ -68,18 +68,17 @@ When authenticators are available on the device and the browser is aware of them
) }}
```
-## Dedicated Authentication Controller
+## Dedicated authentication controller
-{% hint style="info" %}
-**New in v5.3.0:** The Stimulus package now provides dedicated controllers for authentication and registration, in addition to the combined controller.
-{% endhint %}
+!!! info
+
+ **New in v5.3.0:** The Stimulus package now provides dedicated controllers for authentication and registration, in addition to the combined controller.
The new `authentication-controller` provides a focused controller specifically for sign-in flows, with built-in support for conditional UI and better error handling.
-### Basic Usage
+### Basic usage
-{% code title="templates/security/login.html.twig" lineNumbers="true" %}
-```twig
+```twig title="templates/security/login.html.twig" linenums="1"
```
-{% endcode %}
-### Conditional UI (Autofill)
+### Conditional UI (autofill)
The dedicated controller supports conditional UI natively:
-{% code lineNumbers="true" %}
-```twig
+```twig linenums="1"
{{ stimulus_controller('@web-auth/webauthn-stimulus/authentication', {
optionsUrl: path('webauthn.controller.request.request.login'),
resultUrl: path('app_login'),
@@ -124,9 +121,8 @@ The dedicated controller supports conditional UI natively:
conditionalUi: true
}) }}
```
-{% endcode %}
-### Authentication Controller Options
+### Authentication controller options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
@@ -137,7 +133,7 @@ The dedicated controller supports conditional UI natively:
| `conditionalUi` | boolean | `false` | Enable conditional UI (autofill) |
| `verifyAutofillInput` | boolean | `true` | Verify autofill input fields exist |
-### Authentication Controller Targets
+### Authentication controller targets
| Target | Description |
|--------|-------------|
diff --git a/symfony-ux/user-registration.md b/docs/symfony-ux/user-registration.md
similarity index 79%
rename from symfony-ux/user-registration.md
rename to docs/symfony-ux/user-registration.md
index 3fa707e..cd9ba8a 100644
--- a/symfony-ux/user-registration.md
+++ b/docs/symfony-ux/user-registration.md
@@ -1,20 +1,19 @@
-# User Registration
+# 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
+## 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
+### 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
-{% code title="templates/registration/register.html.twig" lineNumbers="true" %}
-```twig
+```twig title="templates/registration/register.html.twig" linenums="1"
```
-{% endcode %}
-### Bundle Configuration
+### Bundle configuration
Configure the bundle to handle new user registration:
-{% code title="config/packages/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/webauthn.yaml" linenums="1"
webauthn:
credential_repository: 'App\Repository\WebauthnCredentialRepository'
user_repository: 'App\Repository\UserRepository'
@@ -72,14 +69,12 @@ webauthn:
result_path: '/register/webauthn'
user_entity_guesser: 'App\Guesser\NewUserEntityGuesser'
```
-{% endcode %}
-### User Entity Guesser
+### User entity guesser
Create a guesser to handle new user registration:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
```
-{% endcode %}
-### Configuration for Existing Users
+### Configuration for existing users
-{% code title="config/packages/webauthn.yaml" lineNumbers="true" %}
-```yaml
+```yaml title="config/packages/webauthn.yaml" linenums="1"
webauthn:
controllers:
enabled: true
@@ -157,13 +148,12 @@ webauthn:
result_path: '/devices/add'
user_entity_guesser: 'Webauthn\Bundle\Security\Guesser\CurrentUserEntityGuesser'
```
-{% endcode %}
-{% hint style="success" %}
-The `CurrentUserEntityGuesser` automatically retrieves the currently authenticated user from the Symfony security context. No custom guesser needed!
-{% endhint %}
+!!! success
+
+ The `CurrentUserEntityGuesser` automatically retrieves the currently authenticated user from the Symfony security context. No custom guesser needed!
-## Stimulus Controller Options
+## Stimulus controller options
The registration controller accepts the following options:
@@ -173,12 +163,11 @@ The registration controller accepts the following 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
+### Auto-register example
Automatically start the registration process when the page loads:
-{% code lineNumbers="true" %}
-```twig
+```twig linenums="1"
{{ stimulus_controller('@web-auth/webauthn-stimulus',
{
autoRegister: true,
@@ -187,24 +176,22 @@ Automatically start the registration process when the page loads:
}
) }}
```
-{% endcode %}
-{% hint style="warning" %}
-Use `autoRegister` carefully as it immediately prompts the user for biometric authentication or security key interaction.
-{% endhint %}
+!!! warning
+
+ Use `autoRegister` carefully as it immediately prompts the user for biometric authentication or security key interaction.
+
+## Dedicated registration controller
-## Dedicated Registration Controller
+!!! info
-{% hint style="info" %}
-**New in v5.3.0:** A dedicated registration controller is available alongside the combined controller.
-{% endhint %}
+ **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
+### Basic usage
-{% code title="templates/registration/register.html.twig" lineNumbers="true" %}
-```twig
+```twig title="templates/registration/register.html.twig" linenums="1"
```
-{% endcode %}
-### Registration Controller Options
+### Registration controller options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
@@ -246,7 +232,7 @@ The dedicated `registration-controller` provides a focused controller for creden
| `successRedirectUri` | string | - | URL to redirect after successful registration |
| `autoRegister` | boolean | `false` | Auto-start registration on page load |
-### Registration Controller Targets
+### Registration controller targets
| Target | Description |
|--------|-------------|
@@ -257,12 +243,11 @@ The dedicated `registration-controller` provides a focused controller for creden
| `authenticatorAttachment` | Select field for authenticator attachment preference |
| `result` | Hidden input for the attestation response |
-## Error Handling
+## Error handling
Handle registration errors in your custom event listeners:
-{% code lineNumbers="true" %}
-```javascript
+```javascript linenums="1"
document.addEventListener('webauthn:registration:error', (event) => {
console.error('Registration failed:', event.detail);
alert('Could not register authenticator: ' + event.detail.message);
@@ -272,9 +257,8 @@ document.addEventListener('webauthn:registration:success', (event) => {
console.log('Registration successful!');
});
```
-{% endcode %}
-## See Also
+## See also
* [User Authentication](user-authentication.md) - Authenticate users with registered authenticators
* [Additional Authenticators](additional-authenticators.md) - Manage multiple authenticators
diff --git a/the-project/contributing.md b/docs/the-project/contributing.md
similarity index 100%
rename from the-project/contributing.md
rename to docs/the-project/contributing.md
diff --git a/the-project/demos.md b/docs/the-project/demos.md
similarity index 88%
rename from the-project/demos.md
rename to docs/the-project/demos.md
index a4191d6..1c9686e 100644
--- a/the-project/demos.md
+++ b/docs/the-project/demos.md
@@ -27,22 +27,18 @@ They use `web-auth/webauthn-lib` alone: no framework, no database, no build step
Run one of them from its own directory:
-{% code lineNumbers="true" %}
-```bash
+```bash linenums="1"
cd basic-demo
./same-origin/run.sh
```
-{% endcode %}
The script installs the dependencies on first run and serves the demo on [http://localhost:8000](http://localhost:8000), which the browsers treat as a secure context. Set the `PORT` variable to serve it elsewhere.
## Running them all at once
-{% code lineNumbers="true" %}
-```bash
+```bash linenums="1"
./run-all.sh
```
-{% endcode %}
Every demo then gets its own port, from 8101 to 8110, so they can be compared side by side. They share the `localhost` Relying Party ID, which means a passkey registered in one of them is offered by the others.
@@ -50,6 +46,6 @@ Every demo then gets its own port, from 8101 to 8110, so they can be compared si
[symfony-demo](https://github.com/web-auth/webauthn-demos/tree/main/symfony-demo) is a complete application built on the bundle, with FrankenPHP, AssetMapper and Tailwind. It targets Symfony 8.1 and PHP 8.4, and shows the firewall, the Doctrine entities, a custom options storage and the Metadata Statement services in one place.
-{% hint style="info" %}
-The pure PHP demos exercise features that are not released yet, so they require `web-auth/webauthn-framework` in its `5.4.x-dev` version. The Symfony application targets the released `^5.3`.
-{% endhint %}
+!!! info
+
+ The pure PHP demos exercise features that are not released yet, so they require `web-auth/webauthn-framework` in its `5.4.x-dev` version. The Symfony application targets the released `^5.3`.
diff --git a/the-project/installation.md b/docs/the-project/installation.md
similarity index 82%
rename from the-project/installation.md
rename to docs/the-project/installation.md
index 393878c..12f6541 100644
--- a/the-project/installation.md
+++ b/docs/the-project/installation.md
@@ -24,9 +24,9 @@ For the Stimulus controllers (frontend integration), pull them straight from npm
php bin/console importmap:require @web-auth/webauthn-stimulus
```
-{% hint style="warning" %}
-The legacy PHP package `web-auth/webauthn-stimulus` is deprecated since v5.3.0 and will be removed in 6.0.0. Install the controllers via npm instead.
-{% endhint %}
+!!! warning
+
+ The legacy PHP package `web-auth/webauthn-stimulus` is deprecated since v5.3.0 and will be removed in 6.0.0. Install the controllers via npm instead.
## Requirements
diff --git a/the-project/the-project.md b/docs/the-project/the-project.md
similarity index 84%
rename from the-project/the-project.md
rename to docs/the-project/the-project.md
index 97775b7..5533e2b 100644
--- a/the-project/the-project.md
+++ b/docs/the-project/the-project.md
@@ -10,7 +10,7 @@ The complete specification can be found on [the W3C dedicated page](https://www.
This framework contains PHP libraries and Symfony bundle to allow developers to integrate that authentication mechanism into their web applications.
-## Class, Constant and Property Names
+## Class, constant and property names
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.
@@ -55,15 +55,15 @@ As an example, the [section 5.2.2 “Web Authentication Assertion”](https://ww
* Conditional Create (new in 5.3.0)
* Auto-register credentials after password authentication
-## Compatible Authenticators
+## Compatible authenticators
As of January 2023, our framework supports every authenticator with full feature and algorithm support, achieving a 100% success rate across all tests. Official FIDO Alliance testing tools have validated its compliance.
We ensure continuous conformity through rigorous unit and functional testing throughout the development process.
-{% hint style="info" %}
-See [https://github.com/herrjemand/awesome-webauthn/pull/61](https://github.com/herrjemand/awesome-webauthn/pull/61) and [https://github.com/herrjemand/awesome-webauthn#server-libs](https://github.com/herrjemand/awesome-webauthn?#server-libraries)
-{% endhint %}
+!!! info
+
+ See [https://github.com/herrjemand/awesome-webauthn/pull/61](https://github.com/herrjemand/awesome-webauthn/pull/61) and [https://github.com/herrjemand/awesome-webauthn#server-libs](https://github.com/herrjemand/awesome-webauthn?#server-libraries)
## Support
@@ -79,6 +79,6 @@ If you feel comfortable writing code, you could try to fix [opened issues where
Do not forget to follow [these best practices](contributing.md).
-{% hint style="danger" %}
-If you think you have found a security issue, **DO NOT open an issue**. [You MUST submit your issue here](https://github.com/web-auth/webauthn-framework/security).
-{% endhint %}
+!!! danger
+
+ If you think you have found a security issue, **DO NOT open an issue**. [You MUST submit your issue here](https://github.com/web-auth/webauthn-framework/security).
diff --git a/the-project/web-browser-support.md b/docs/the-project/web-browser-support.md
similarity index 95%
rename from the-project/web-browser-support.md
rename to docs/the-project/web-browser-support.md
index 393b014..3e06afa 100644
--- a/the-project/web-browser-support.md
+++ b/docs/the-project/web-browser-support.md
@@ -2,7 +2,7 @@
description: Adoption by web browsers
---
-# Web Browser Support
+# Web browser support
WebAuthn is now supported by all main web browsers:
diff --git a/webauthn-in-a-nutshell/attestation-and-metadata-statement.md b/docs/webauthn-in-a-nutshell/attestation-and-metadata-statement.md
similarity index 70%
rename from webauthn-in-a-nutshell/attestation-and-metadata-statement.md
rename to docs/webauthn-in-a-nutshell/attestation-and-metadata-statement.md
index 006102b..5808092 100644
--- a/webauthn-in-a-nutshell/attestation-and-metadata-statement.md
+++ b/docs/webauthn-in-a-nutshell/attestation-and-metadata-statement.md
@@ -1,14 +1,14 @@
-# Metadata Statement
+# Metadata statement
-{% hint style="info" %}
-This page is about an advanced feature. You can skip it if you are new with WebAuthn.
-{% endhint %}
+!!! info
-{% hint style="danger" %}
-Disclaimer: you **should not** ask for the Attestation Statement unless you want to play with it or you are working on an application that requires a high level of trust (e.g. Banking/Financial Company, Government Agency...).
-{% endhint %}
+ This page is about an advanced feature. You can skip it if you are new with WebAuthn.
-## Attestation Statement
+!!! danger
+
+ Disclaimer: you **should not** ask for the Attestation Statement unless you want to play with it or you are working on an application that requires a high level of trust (e.g. Banking/Financial Company, Government Agency...).
+
+## Attestation statement
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:
@@ -19,12 +19,12 @@ During the Attestation Ceremony (i.e. the registration of the authenticator), yo
* **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.
* **Compound Attestation** (new in 5.3.0): A combination of multiple attestation statements that allows authenticators to provide multiple levels of attestation in a single response.
-## Metadata Statement
+## Metadata statement
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](https://fidoalliance.org/metadata) that allows fetching statements on-demand, but some of them may be provided by other means.
-{% hint style="danger" %}
-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.
-{% endhint %}
+!!! danger
+
+ 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.
diff --git a/webauthn-in-a-nutshell/authenticators.md b/docs/webauthn-in-a-nutshell/authenticators.md
similarity index 74%
rename from webauthn-in-a-nutshell/authenticators.md
rename to docs/webauthn-in-a-nutshell/authenticators.md
index c210300..8addb2f 100644
--- a/webauthn-in-a-nutshell/authenticators.md
+++ b/docs/webauthn-in-a-nutshell/authenticators.md
@@ -8,27 +8,30 @@ An Authenticator is a cryptographic entity used to generate a public key credent
Authenticators may have additional features such as PIN code or biometric sensors (fingerprint, facial recognition…) that offer user verification.
-.jpeg>)
+
-## Roaming Authenticators
+## Roaming authenticators
The roaming authenticator may have different forms. The most common form is a USB device the user plugs into their 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.
-
+
-## Platform Authenticators
+## Platform authenticators
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.
-.png>)
+
-
+
### Passkey
-
Passkey Logo
+
+ { width="188" }
+ Passkey Logo
+
Passkeys are virtual authenticators that have extended capabilities and in particular:
diff --git a/webauthn-in-a-nutshell/ceremonies.md b/docs/webauthn-in-a-nutshell/ceremonies.md
similarity index 84%
rename from webauthn-in-a-nutshell/ceremonies.md
rename to docs/webauthn-in-a-nutshell/ceremonies.md
index ec989d9..d7f1543 100644
--- a/webauthn-in-a-nutshell/ceremonies.md
+++ b/docs/webauthn-in-a-nutshell/ceremonies.md
@@ -6,11 +6,11 @@ description: Registration and Authentication process overview
In WebAuthn, authentication workflows are called "ceremonies" - a term that emphasizes the formal, cryptographic nature of these operations. There are two main ceremonies that form the foundation of WebAuthn authentication.
-## The Two Ceremonies
+## The two ceremonies
WebAuthn defines two distinct ceremonies:
-### 1. Attestation Ceremony (Registration)
+### 1. Attestation ceremony (registration)
**Also called:** Creation Ceremony, Registration Ceremony
@@ -21,7 +21,7 @@ WebAuthn defines two distinct ceremonies:
* Adding an additional authenticator to an existing account
* Registering a backup security key
-### 2. Assertion Ceremony (Authentication)
+### 2. Assertion ceremony (authentication)
**Also called:** Request Ceremony, Authentication Ceremony, Login Ceremony
@@ -32,11 +32,11 @@ WebAuthn defines two distinct ceremonies:
* Step-up authentication for sensitive operations
* Re-authentication after session timeout
-## Common Workflow Pattern
+## Common workflow pattern
Both ceremonies follow a similar two-step pattern:
-### Step 1: Options Creation
+### Step 1: Options creation
The Relying Party (your server) creates options that:
* Define parameters for the operation
@@ -44,7 +44,7 @@ The Relying Party (your server) creates options that:
* Specify requirements and preferences
* Are sent to the browser/authenticator
-### Step 2: Response Verification
+### Step 2: Response verification
The authenticator:
* Prompts the user for interaction
@@ -52,22 +52,21 @@ The authenticator:
* Returns a signed response
* The server validates the response
-{% hint style="info" %}
-User interaction varies based on authenticator capabilities and your configuration. It can range from a simple button touch to biometric authentication (fingerprint, facial recognition, PIN code).
-{% endhint %}
+!!! info
-## Attestation Ceremony (Registration)
+ User interaction varies based on authenticator capabilities and your configuration. It can range from a simple button touch to biometric authentication (fingerprint, facial recognition, PIN code).
+
+## Attestation ceremony (registration)
The attestation ceremony registers a new authenticator credential for a user.
-.png>)
+
-### Attestation Flow Breakdown
+### Attestation flow breakdown
-#### Server Side (Step 1)
+#### Server side (step 1)
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// 1. Create PublicKeyCredentialCreationOptions
$publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::create(
rp: $rpEntity, // Your application identity
@@ -83,9 +82,8 @@ $session->set('webauthn_creation_challenge', $publicKeyCredentialCreationOptions
// 3. Send options to browser as JSON
return new JsonResponse($publicKeyCredentialCreationOptions);
```
-{% endcode %}
-#### Client Side (Browser)
+#### Client side (browser)
```javascript
// 1. Receive options from server
@@ -103,10 +101,9 @@ await fetch('/webauthn/register', {
});
```
-#### Server Side (Step 2)
+#### Server side (step 2)
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// 1. Load the attestation response
$publicKeyCredential = $serializer->deserialize(
$request->getContent(),
@@ -124,9 +121,8 @@ $credentialSource = $attestationValidator->check(
// 3. Save the new credential
$credentialRepository->saveCredentialSource($credentialSource);
```
-{% endcode %}
-### What Happens During Attestation?
+### What happens during attestation?
1. **Challenge Generation**: Server creates a random challenge to prevent replay attacks
2. **User Prompt**: Browser asks user to interact with authenticator
@@ -135,7 +131,7 @@ $credentialRepository->saveCredentialSource($credentialSource);
5. **Verification**: Server verifies signatures and stores the public key
6. **Storage**: Credential is saved and associated with the user account
-### Attestation Data Contains
+### Attestation data contains
* **Public Key**: Used to verify future signatures
* **Credential ID**: Unique identifier for this credential
@@ -143,18 +139,17 @@ $credentialRepository->saveCredentialSource($credentialSource);
* **Signature Counter**: Used to detect cloned authenticators
* **Attestation Statement**: Optional proof of authenticator authenticity
-## Assertion Ceremony (Authentication)
+## Assertion ceremony (authentication)
The assertion ceremony authenticates a user with a previously registered credential.
-
+
-### Assertion Flow Breakdown
+### Assertion flow breakdown
-#### Server Side (Step 1)
+#### Server side (step 1)
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// 1. Get user's registered credentials
$credentials = $credentialRepository->findAllForUserEntity($userEntity);
$allowedCredentials = array_map(
@@ -175,9 +170,8 @@ $session->set('webauthn_request_challenge', $publicKeyCredentialRequestOptions->
// 4. Send options to browser
return new JsonResponse($publicKeyCredentialRequestOptions);
```
-{% endcode %}
-#### Client Side (Browser)
+#### Client side (browser)
```javascript
// 1. Receive options from server
@@ -195,10 +189,9 @@ await fetch('/webauthn/login', {
});
```
-#### Server Side (Step 2)
+#### Server side (step 2)
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// 1. Load the assertion response
$publicKeyCredential = $serializer->deserialize(
$request->getContent(),
@@ -226,9 +219,8 @@ $credentialRepository->saveCredentialSource($updatedCredential);
// 5. Log the user in
$session->set('user_id', $userEntity->id);
```
-{% endcode %}
-### What Happens During Assertion?
+### What happens during assertion?
1. **Challenge Generation**: Server creates a new random challenge
2. **Credential Selection**: Browser identifies available credentials
@@ -238,14 +230,14 @@ $session->set('user_id', $userEntity->id);
6. **Counter Check**: Server verifies counter to detect cloning
7. **Authentication**: If valid, user is logged in
-### Assertion Data Contains
+### Assertion data contains
* **Authenticator Data**: Flags, counter, extensions
* **Client Data**: Origin, challenge, type
* **Signature**: Cryptographic proof using the private key
* **User Handle**: Optional user identifier (for usernameless authentication)
-## Key Differences Between Ceremonies
+## Key differences between ceremonies
| Aspect | Attestation (Registration) | Assertion (Authentication) |
|--------|---------------------------|---------------------------|
@@ -257,14 +249,13 @@ $session->set('user_id', $userEntity->id);
| **Validator Class** | `AuthenticatorAttestationResponseValidator` | `AuthenticatorAssertionResponseValidator` |
| **User Experience** | "Register security key" | "Use security key to sign in" |
-## Security Considerations
+## Security considerations
-### Challenge Uniqueness
+### Challenge uniqueness
Both ceremonies require a unique, random challenge:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// Always generate a fresh challenge
$challenge = random_bytes(32); // 32 bytes = 256 bits of entropy
@@ -272,14 +263,12 @@ $challenge = random_bytes(32); // 32 bytes = 256 bits of entropy
// NEVER use predictable challenges (like timestamps)
// NEVER skip challenge verification
```
-{% endcode %}
-### Challenge Storage
+### Challenge storage
Store challenges temporarily in server-side sessions:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// Store during Step 1 (options creation)
$session->set('webauthn_challenge', base64_encode($challenge));
$session->set('webauthn_challenge_expires', time() + 60); // 60 second timeout
@@ -292,14 +281,12 @@ if (time() > $session->get('webauthn_challenge_expires')) {
throw new \Exception('Challenge expired');
}
```
-{% endcode %}
-### Origin Validation
+### Origin validation
Always verify the origin matches your domain:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// Server must verify origin during validation
$credentialSource = $attestationValidator->check(
$publicKeyCredential->response,
@@ -307,18 +294,17 @@ $credentialSource = $attestationValidator->check(
'https://example.com' // Must match your actual domain
);
```
-{% endcode %}
-## Common Pitfalls
+## Common pitfalls
-### ❌ Challenge Reuse
+### ❌ Challenge reuse
```php
// WRONG: Reusing the same challenge
$challenge = 'my-static-challenge'; // Never do this!
```
-### ❌ Skipping Verification
+### ❌ Skipping verification
```php
// WRONG: Trusting client data without validation
@@ -326,7 +312,7 @@ $credentialId = $_POST['credentialId'];
$user = $userRepository->find($credentialId); // DON'T!
```
-### ❌ Incorrect Origin
+### ❌ Incorrect origin
```php
// WRONG: Verifying with wrong domain
@@ -334,14 +320,13 @@ $attestationValidator->check($response, $options, 'http://localhost');
// But the actual domain is https://example.com
```
-## Best Practices
+## Best practices
-### ✅ Use Timeouts
+### ✅ Use timeouts
Set reasonable timeouts for user interaction:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
$publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::create(
rp: $rpEntity,
user: $userEntity,
@@ -349,14 +334,12 @@ $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::create
timeout: 60000 // 60 seconds in milliseconds
);
```
-{% endcode %}
-### ✅ Handle Errors Gracefully
+### ✅ Handle errors gracefully
Provide helpful error messages:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
try {
$credentialSource = $attestationValidator->check(/* ... */);
} catch (\Webauthn\Exception\InvalidDataException $e) {
@@ -366,22 +349,19 @@ try {
], 400);
}
```
-{% endcode %}
-### ✅ Store Credential Metadata
+### ✅ Store credential metadata
Keep track of when and how credentials were registered:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
$credential->createdAt = new \DateTimeImmutable();
$credential->lastUsedAt = null;
$credential->name = 'My YubiKey'; // Allow users to name their keys
$credential->transports = ['usb', 'nfc']; // Store transport hints
```
-{% endcode %}
-## See Also
+## See also
* [Authenticators](authenticators.md) - Learn about different authenticator types
* [User Verification](user-verification.md) - Understand UP vs UV flags
diff --git a/webauthn-in-a-nutshell/extensions.md b/docs/webauthn-in-a-nutshell/extensions.md
similarity index 81%
rename from webauthn-in-a-nutshell/extensions.md
rename to docs/webauthn-in-a-nutshell/extensions.md
index 69b84f2..f8c1ee3 100644
--- a/webauthn-in-a-nutshell/extensions.md
+++ b/docs/webauthn-in-a-nutshell/extensions.md
@@ -1,12 +1,12 @@
# Extensions
-{% hint style="info" %}
-This page is about an advanced feature. You can skip it if you are new with WebAuthn.
-{% endhint %}
+!!! info
+
+ This page is about an advanced feature. You can skip it if you are new with WebAuthn.
WebAuthn extensions are optional features that extend the core WebAuthn specification to support additional use cases and capabilities. They allow authenticators and browsers to provide extra functionality beyond basic authentication.
-## What Are Extensions?
+## What are extensions?
Extensions provide a standardized way to:
@@ -15,20 +15,20 @@ Extensions provide a standardized way to:
* Enable specific features like credential protection or large blob storage
* Maintain backward compatibility with legacy systems (like U2F)
-## How Extensions Work
+## How extensions work
Extensions work in both directions:
1. **Extension Inputs** - Sent from the Relying Party to the authenticator via the browser
2. **Extension Outputs** - Returned from the authenticator to the Relying Party after processing
-{% hint style="warning" %}
-**Important**: Authenticators may ignore extension inputs if they don't support them. Extension outputs are **not guaranteed** and your application should handle their absence gracefully.
-{% endhint %}
+!!! warning
+
+ **Important**: Authenticators may ignore extension inputs if they don't support them. Extension outputs are **not guaranteed** and your application should handle their absence gracefully.
-## Common WebAuthn Extensions
+## Common WebAuthn extensions
-### Standard Extensions
+### Standard extensions
The most commonly used WebAuthn extensions include:
@@ -49,7 +49,7 @@ All standard extensions are listed in the **IANA WebAuthn Extensions Registry**:
[https://www.iana.org/assignments/webauthn/webauthn.xhtml](https://www.iana.org/assignments/webauthn/webauthn.xhtml)
-## Library Support
+## Library support
This library provides:
@@ -57,16 +57,15 @@ This library provides:
* ✅ **AppID extension** - Built-in support for U2F backward compatibility
* ⚠️ **Custom extensions** - You need to implement extension handlers for other extensions
-{% hint style="warning" %}
-Most extensions require custom implementation. It is up to you to create the extension handlers depending on which extensions you want to support.
-{% endhint %}
+!!! warning
-## Basic Usage Example
+ Most extensions require custom implementation. It is up to you to create the extension handlers depending on which extensions you want to support.
+
+## Basic usage example
Here's how to add an extension input when creating credential options:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
response->getAuthenticatorData()->extensions;
@@ -103,16 +100,14 @@ if (isset($extensionOutputs['credProps']['rk'])) {
echo $isDiscoverable ? 'Resident key created' : 'Non-resident key';
}
```
-{% endcode %}
-## Practical Use Cases
+## Practical use cases
-### 1. U2F Migration (appid Extension)
+### 1. U2F migration (appid extension)
Allow existing U2F credentials to work with your WebAuthn implementation:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
$extensions = AuthenticationExtensions::create([
AuthenticationExtension::create('appid', 'https://example.com')
]);
@@ -122,14 +117,12 @@ $publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::create(
extensions: $extensions
);
```
-{% endcode %}
-### 2. Credential Properties (credProps Extension)
+### 2. Credential properties (credProps extension)
Determine if a credential supports resident keys (usernameless authentication):
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
$extensions = AuthenticationExtensions::create([
AuthenticationExtension::create('credProps', true)
]);
@@ -137,14 +130,12 @@ $extensions = AuthenticationExtensions::create([
// After registration, check:
$rk = $extensionOutputs['credProps']['rk'] ?? false;
```
-{% endcode %}
-### 3. Large Blob Storage (largeBlob Extension)
+### 3. Large blob storage (largeBlob extension)
Store additional encrypted data with the credential:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// Write blob during registration
$extensions = AuthenticationExtensions::create([
AuthenticationExtension::create('largeBlob', [
@@ -153,18 +144,16 @@ $extensions = AuthenticationExtensions::create([
])
]);
```
-{% endcode %}
-### 4. Pseudo-Random Function (prf Extension)
+### 4. Pseudo-random function (prf extension)
+
+!!! info
-{% hint style="info" %}
-PRF extension support was added in version 5.2.0
-{% endhint %}
+ PRF extension support was added in version 5.2.0
The PRF (Pseudo-Random Function) extension allows you to generate cryptographic keys from authenticator secrets. This is particularly useful for encryption scenarios:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// Request PRF during authentication
$extensions = AuthenticationExtensions::create([
AuthenticationExtension::create('prf', [
@@ -179,11 +168,10 @@ $publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::create(
extensions: $extensions
);
```
-{% endcode %}
-## Implementation Considerations
+## Implementation considerations
-### Check Extension Support
+### Check extension support
Not all authenticators support all extensions. Always:
@@ -191,7 +179,7 @@ Not all authenticators support all extensions. Always:
2. Have fallback behavior when extensions aren't supported
3. Don't rely on extensions for critical security features
-### Custom Extension Handlers
+### Custom extension handlers
For extensions beyond `appid`, you'll need to implement:
@@ -200,7 +188,7 @@ For extensions beyond `appid`, you'll need to implement:
See the [Pure PHP Extensions Guide](../pure-php/advanced-behaviours/extensions.md) or [Symfony Bundle Extensions Guide](../symfony-bundle/advanced-behaviors/extensions.md) for detailed implementation instructions.
-## When to Use Extensions
+## When to use extensions
Use extensions when you need:
@@ -215,7 +203,7 @@ Avoid extensions if:
* ❌ You need universal compatibility (not all devices support extensions)
* ❌ You're just getting started with WebAuthn
-## See Also
+## See also
* [Pure PHP Extensions Implementation](../pure-php/advanced-behaviours/extensions.md) - Detailed extension implementation
* [Symfony Extensions Configuration](../symfony-bundle/advanced-behaviors/extensions.md) - Framework-specific setup
diff --git a/webauthn-in-a-nutshell/user-verification.md b/docs/webauthn-in-a-nutshell/user-verification.md
similarity index 85%
rename from webauthn-in-a-nutshell/user-verification.md
rename to docs/webauthn-in-a-nutshell/user-verification.md
index ee8684a..0b85895 100644
--- a/webauthn-in-a-nutshell/user-verification.md
+++ b/docs/webauthn-in-a-nutshell/user-verification.md
@@ -2,11 +2,11 @@
description: Prove to me who you claim to be!
---
-# User Verification
+# User verification
User verification is a WebAuthn feature that confirms the identity of the actual user, not just possession of the authenticator. It answers the question: "Is the person using this authenticator really the authorized user?"
-## What is User Verification?
+## What is user verification?
User verification requires the user to prove their identity through:
@@ -17,7 +17,7 @@ User verification requires the user to prove their identity through:
The goal is to **distinguish individual users** and ensure that the person authenticating is the legitimate account owner, not just someone who stole or found the authenticator.
-## User Presence vs User Verification
+## User presence vs user verification
It's important to understand the difference:
@@ -33,7 +33,7 @@ It's important to understand the difference:
**User Verification** goes further by confirming the specific identity of that person.
-## User Verification Requirements
+## User verification requirements
You can configure the user verification requirement when creating credential options. There are three possible values:
@@ -41,8 +41,7 @@ You can configure the user verification requirement when creating credential opt
The operation **requires** user verification and will **fail** if the authenticator does not set the `UV` flag.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
use Webauthn\AuthenticatorSelectionCriteria;
use Webauthn\PublicKeyCredentialCreationOptions;
@@ -57,7 +56,6 @@ $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::create
authenticatorSelection: $authenticatorSelection
);
```
-{% endcode %}
**Use when:**
* High-security applications (banking, healthcare)
@@ -68,13 +66,11 @@ $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::create
The operation **prefers** user verification but will **not fail** if the authenticator doesn't provide it.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
$authenticatorSelection = AuthenticatorSelectionCriteria::create(
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED
);
```
-{% endcode %}
This is the **recommended default** for most applications. It uses user verification when available but doesn't exclude authenticators that don't support it.
@@ -87,13 +83,11 @@ This is the **recommended default** for most applications. It uses user verifica
The operation **discourages** user verification to minimize disruption to the user flow.
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
$authenticatorSelection = AuthenticatorSelectionCriteria::create(
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED
);
```
-{% endcode %}
**Use when:**
* Low-risk operations
@@ -101,18 +95,17 @@ $authenticatorSelection = AuthenticatorSelectionCriteria::create(
* Simple presence verification is sufficient
* Testing or demonstration purposes
-## Checking the UV Flag
+## Checking the UV flag
The `userVerification` setting in the credential options is a **request** sent to the authenticator, not a guarantee. The `CheckUserVerification` ceremony step enforces it against the options that were actually emitted to the client — so if those options were built with a weaker requirement than your profile (for example, because [Client Override Policy](../symfony-bundle/advanced-behaviors/client-override-policy.md) is configured to allow a client to lower it), the ceremony will pass even though no verification took place.
-{% hint style="warning" %}
-**Mandatory for sensitive operations.** Do not rely solely on `userVerification: required` in the profile to gate access to privileged actions (admin, payment, secret rotation, AAL2/AAL3 compliance). Always re-check the `UV` flag on the returned authenticator data after a successful ceremony. This is your final, authoritative signal that user verification actually occurred.
-{% endhint %}
+!!! warning
+
+ **Mandatory for sensitive operations.** Do not rely solely on `userVerification: required` in the profile to gate access to privileged actions (admin, payment, secret rotation, AAL2/AAL3 compliance). Always re-check the `UV` flag on the returned authenticator data after a successful ceremony. This is your final, authoritative signal that user verification actually occurred.
After authentication or registration, inspect the flag on the response's authenticator data and decide whether to proceed:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// $authenticatorResponse is the AuthenticatorAssertionResponse or
// AuthenticatorAttestationResponse that was just successfully validated.
$authenticatorData = $authenticatorResponse instanceof AuthenticatorAssertionResponse
@@ -127,23 +120,19 @@ if (! $authenticatorData->isUserVerified()) {
// Safe to proceed with the privileged operation
```
-{% endcode %}
You can also read the raw flags if you need fine-grained checks (for example combining `UV` with `BS`/`BE` for backup-eligibility policies):
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
$flags = $authenticatorData->getFlags();
$uvFlag = (ord($flags) & 0x04) !== 0; // UV flag is bit 2
```
-{% endcode %}
-## Practical Examples
+## Practical examples
-### Example 1: High-Security Banking App
+### Example 1: High-security banking app
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// Require user verification for all operations
$authenticatorSelection = AuthenticatorSelectionCriteria::create(
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED
@@ -157,12 +146,10 @@ $options = PublicKeyCredentialCreationOptions::create(
authenticatorSelection: $authenticatorSelection
);
```
-{% endcode %}
-### Example 2: General Web Application
+### Example 2: General web application
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// Prefer user verification but don't require it
$authenticatorSelection = AuthenticatorSelectionCriteria::create(
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED
@@ -176,21 +163,18 @@ $options = PublicKeyCredentialCreationOptions::create(
authenticatorSelection: $authenticatorSelection
);
```
-{% endcode %}
-### Example 3: Fast Re-authentication
+### Example 3: Fast re-authentication
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// For quick re-auth on already logged-in user, discourage UV
$options = PublicKeyCredentialRequestOptions::create(
challenge: random_bytes(32),
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED
);
```
-{% endcode %}
-## Device Capabilities
+## Device capabilities
Different authenticators have different user verification capabilities:
@@ -203,28 +187,27 @@ Different authenticators have different user verification capabilities:
| **YubiKey Bio** | Fingerprint |
| **Basic security keys** | None (only user presence) |
-{% hint style="info" %}
-When you set `userVerification: required`, only authenticators capable of user verification will be available to the user during registration or authentication.
-{% endhint %}
+!!! info
+
+ When you set `userVerification: required`, only authenticators capable of user verification will be available to the user during registration or authentication.
-## Best Practices
+## Best practices
-### For Registration
+### For registration
1. **Use `preferred` as default** - Balances security and compatibility
2. **Use `required` for sensitive accounts** - Banking, healthcare, admin accounts
3. **Inform users** - Explain that biometric/PIN improves security
-### For Authentication
+### For authentication
1. **Match registration settings** - If registered with `required`, authenticate with `required`
2. **Allow `preferred` for convenience** - Users can choose faster auth if device supports it
3. **Document your policy** - Make clear what level of verification is expected
-### Conditional Logic
+### Conditional logic
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
// Require UV only for privileged operations
$isAdminAction = ($user->getRole() === 'ROLE_ADMIN');
@@ -237,9 +220,8 @@ $options = PublicKeyCredentialRequestOptions::create(
userVerification: $userVerification
);
```
-{% endcode %}
-## Common Issues
+## Common issues
### Issue: "User verification not supported"
@@ -253,19 +235,18 @@ $options = PublicKeyCredentialRequestOptions::create(
**Solution**: Use `discouraged` or `preferred` for routine operations, save `required` for sensitive actions.
-## Security Considerations
+## Security considerations
* ✅ **Higher assurance** - UV provides stronger identity verification than UP alone
* ✅ **Phishing resistant** - Even if an attacker steals the authenticator, they need the PIN/biometric
* ⚠️ **Not foolproof** - Biometric systems can have false positives; PINs can be observed
* ⚠️ **Device dependent** - Relying solely on UV limits compatible devices
-## Constants Reference
+## Constants reference
The library provides convenient constants in the `AuthenticatorSelectionCriteria` class:
-{% code lineNumbers="true" %}
-```php
+```php linenums="1"
use Webauthn\AuthenticatorSelectionCriteria;
// For registration (attestation)
@@ -276,9 +257,8 @@ AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED
// These same constants can be used for authentication (assertion)
// in PublicKeyCredentialRequestOptions
```
-{% endcode %}
-## See Also
+## See also
* [Authenticators](authenticators.md) - Learn about different authenticator types
* [Authenticator Selection Criteria](../pure-php/advanced-behaviours/authenticator-selection-criteria.md) - Detailed configuration
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 0000000..9c195cb
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,240 @@
+site_name: WebAuthn Framework
+site_description: WebAuthn and passkeys for PHP and Symfony. Registration and authentication ceremonies, the Symfony bundle and the Symfony UX components, after WebAuthn Level 3.
+site_url: https://webauthn-doc.spomky-labs.com/
+site_author: Spomky-Labs
+repo_url: https://github.com/web-auth/webauthn-framework
+repo_name: web-auth/webauthn-framework
+edit_uri: https://github.com/web-auth/doc/edit/5.3/docs/
+
+docs_dir: docs
+site_dir: site
+
+# The GitBook table of contents. It stays while GitBook serves the domain, and the navigation below says the
+# same thing in the words of MkDocs.
+exclude_docs: |
+ SUMMARY.md
+
+theme:
+ name: material
+ custom_dir: docs/overrides
+ language: en
+ # Space Grotesk and IBM Plex Mono are served from assets/fonts, declared in slabs.css: the theme loads
+ # no font of its own.
+ font: false
+ icon:
+ repo: fontawesome/brands/github
+ edit: material/pencil-outline
+ annotation: material/plus-circle
+ favicon: assets/brand/favicon.svg
+ palette:
+ - media: "(prefers-color-scheme)"
+ toggle:
+ icon: material/brightness-auto
+ name: Follow the system theme
+ - media: "(prefers-color-scheme: light)"
+ scheme: default
+ primary: custom
+ accent: custom
+ toggle:
+ icon: material/weather-sunny
+ name: Switch to the dark theme
+ - media: "(prefers-color-scheme: dark)"
+ scheme: slate
+ primary: custom
+ accent: custom
+ toggle:
+ icon: material/weather-night
+ name: Switch to the light theme
+ features:
+ - navigation.instant
+ - navigation.instant.progress
+ - navigation.tracking
+ - navigation.sections
+ - navigation.indexes
+ - navigation.top
+ - navigation.footer
+ - toc.follow
+ - search.suggest
+ - search.highlight
+ - search.share
+ - content.code.copy
+ - content.code.annotate
+ - content.tabs.link
+ - content.tooltips
+ - content.action.edit
+
+# The tokens of the Slabs design kit, as they are, then the rules that apply them.
+extra_css:
+ - assets/stylesheets/slabs-tokens.css
+ - assets/stylesheets/slabs.css
+
+plugins:
+ - search
+ # Every asset is served from this site. Should a page ever reference an external one, it is downloaded
+ # at build time: nothing a reader loads reaches a third party.
+ - privacy
+ - git-revision-date-localized:
+ enable_creation_date: false
+ type: date
+ fallback_to_build_date: true
+ - redirects:
+ redirect_maps: {}
+
+markdown_extensions:
+ - abbr
+ - admonition
+ - attr_list
+ - def_list
+ - footnotes
+ - md_in_html
+ - tables
+ - toc:
+ title: On this page
+ permalink: true
+ permalink_title: Link to this section
+ toc_depth: 3
+ - pymdownx.betterem
+ - pymdownx.caret
+ - pymdownx.details
+ - pymdownx.emoji:
+ emoji_index: !!python/name:material.extensions.emoji.twemoji
+ emoji_generator: !!python/name:material.extensions.emoji.to_svg
+ - pymdownx.highlight:
+ anchor_linenums: true
+ # A PHP block of these pages starts without "
+
+
+
+
+
+ Page not found · WebAuthn Framework
+
+
+
+
+
+
+
+
+
Looking for this page in the latest version…
+
+
+
Page not found
+
This page has moved or never existed. Search, or start from one of these: