### Install webauthn-framework via Composer Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/docs/secure-payment-confirmation.md Install the core webauthn-framework package, which includes SPC support. ```bash composer require web-auth/webauthn-framework ``` -------------------------------- ### Install @web-auth/webauthn-stimulus Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/PAYMENT_CONTROLLER.md Install the package that provides the PaymentController Stimulus controller. ```bash npm install @web-auth/webauthn-stimulus ``` -------------------------------- ### Install web-auth/webauthn-stimulus with Composer and Yarn Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/doc/index.rst Install the Symfony UX Webauthn bundle with Composer and Symfony Flex, then install JavaScript dependencies and compile assets with Yarn. Requires Symfony UX to be configured in the app and at least version 3.0 of @symfony/stimulus-bridge in package.json. ```terminal $ composer require web-auth/webauthn-stimulus # Don't forget to install the JavaScript dependencies as well and compile $ yarn install --force $ yarn encore dev ``` -------------------------------- ### Install with Symfony AssetMapper Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md Installs the npm package via Symfony AssetMapper's importmap. This is the recommended way to install the package for Symfony projects. ```bash php bin/console importmap:require @web-auth/webauthn-stimulus ``` -------------------------------- ### Development commands for npm Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md Development commands for installing dependencies, running tests, linting, and formatting code. ```bash # Install dependencies npm install # Run tests npm test # Lint code npm run lint # Format code npm run format ``` -------------------------------- ### Standalone Setup for Payment Data Validation Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/docs/secure-payment-confirmation.md Configures ClientDataCollectorManager and ExtensionOutputCheckerHandler for standalone verification of payment client data and browser-bound signature. ```php use Webauthn\AuthenticationExtensions\ExtensionOutputCheckerHandler; use Webauthn\AuthenticationExtensions\PaymentExtensionOutputChecker; use Webauthn\CeremonyStep\CeremonyStepManagerFactory; use Webauthn\ClientDataCollector\ClientDataCollectorManager; use Webauthn\ClientDataCollector\PaymentClientDataCollector; use Webauthn\ClientDataCollector\WebauthnAuthenticationCollector; $clientDataManager = new ClientDataCollectorManager([ new WebauthnAuthenticationCollector(), new PaymentClientDataCollector($serializer), ]); $extensionHandler = ExtensionOutputCheckerHandler::create(); $extensionHandler->add(new PaymentExtensionOutputChecker()); $factory = new CeremonyStepManagerFactory(); $factory->setClientDataCollectorManager($clientDataManager); $factory->setExtensionOutputCheckerHandler($extensionHandler); ``` -------------------------------- ### Use stimulus_controller() Twig helper Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md Example of using the stimulus_controller() Twig helper with the registered controllers. This works after registering the controllers as shown in the JavaScript snippet. ```twig
{# ... #}
``` -------------------------------- ### Create Payment Extension for Authentication Options Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/docs/secure-payment-confirmation.md Shows PHP code to create a payment extension for PublicKeyCredentialRequestOptions, including payment amount, instrument, and authentication configuration. ```php use Webauthn\AuthenticationExtensions\PaymentExtension; use Webauthn\SecurePaymentConfirmation\PaymentCurrencyAmount; use Webauthn\SecurePaymentConfirmation\PaymentCredentialInstrument; use Webauthn\PublicKeyCredentialRequestOptions; // Create payment data $amount = PaymentCurrencyAmount::create('USD', '99.99'); $instrument = PaymentCredentialInstrument::create( displayName: 'Visa •••• 1234', icon: 'https://example.com/visa-icon.png', iconMustBeShown: true ); // Create payment extension $paymentExtension = PaymentExtension::authenticate( rpId: 'example.com', topOrigin: 'https://merchant.example.com', total: $amount, instrument: $instrument, payeeName: 'Merchant Store', payeeOrigin: 'https://merchant.example.com', ); // Add to authentication options $publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::create( challenge: random_bytes(32), rpId: 'example.com', extensions: new AuthenticationExtensions([$paymentExtension]) ); ``` -------------------------------- ### Running Specific PHPUnit Tests Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/CLAUDE.md Use the vendor binary with the --filter option to run specific tests. The first command filters by test class name, and the second runs a specific test file directly. ```bash vendor/bin/phpunit --filter=TestClassName tests/ vendor/bin/phpunit tests/framework/SomeSpecificTest.php ``` -------------------------------- ### JavaScript Development Commands via npm Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/CLAUDE.md Use these npm commands for JavaScript development: running tests, linting, and formatting. The lint:fix and format:fix variants automatically fix issues. ```bash # Run all JavaScript tests npm test # Linting npm run lint # Check linting npm run lint:fix # Auto-fix linting issues # Code formatting npm run format # Check formatting npm run format:fix # Auto-fix formatting ``` -------------------------------- ### PHP Backend Controller for SPC Options and Verification Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/docs/secure-payment-confirmation.md This PHP controller demonstrates how to create SPC authentication options with a PaymentExtension using server-validated transaction data, and how to verify the assertion. It includes security checks to prevent client-side tampering and uses the standard validator, which is wired with PaymentClientDataCollector and PaymentExtensionOutputChecker. ```php getContent(), true); // SECURITY: Fetch payment details from database using transaction ID // This prevents client-side tampering of amounts/payee $transactionId = $data['transactionId'] ?? null; if (!$transactionId) { return new JsonResponse(['error' => 'Transaction ID required'], 400); } // Get transaction from secure database $transaction = $this->transactionRepository->findOneBy(['id' => $transactionId]); if (!$transaction || $transaction->getUserId() !== $this->getUser()->getId()) { return new JsonResponse(['error' => 'Transaction not found'], 404); } // Verify transaction is in pending state if ($transaction->getStatus() !== 'pending') { return new JsonResponse(['error' => 'Transaction already processed'], 400); } // Create payment extension with SERVER-VALIDATED data $amount = PaymentCurrencyAmount::create( $transaction->getCurrency(), $transaction->getAmount() ); $instrument = PaymentCredentialInstrument::create( $transaction->getPaymentMethod()->getDisplayName(), $transaction->getPaymentMethod()->getIconUrl() ); $paymentExtension = PaymentExtension::authenticate( rpId: 'example.com', topOrigin: $transaction->getMerchantOrigin(), total: $amount, instrument: $instrument, payeeName: $transaction->getPayeeName(), payeeOrigin: $transaction->getPayeeOrigin(), ); // Create authentication options $options = PublicKeyCredentialRequestOptions::create( challenge: random_bytes(32), rpId: 'example.com', allowCredentials: $this->getUserCredentials(), extensions: new AuthenticationExtensions([$paymentExtension]) ); // Store challenge and transaction ID in session for verification $_SESSION['payment_challenge'] = base64_encode($options->challenge); $_SESSION['payment_transaction_id'] = $transactionId; return new JsonResponse($options); } public function verify(Request $request): JsonResponse { $data = json_decode($request->getContent(), true); // Deserialize and verify the credential $publicKeyCredential = $this->serializer->deserialize( json_encode($data), PublicKeyCredential::class, 'json' ); // Verify the assertion. The bundle has wired both the // PaymentClientDataCollector (validates clientDataJSON.payment) and // the PaymentExtensionOutputChecker (validates browserBoundSignature // is present), so the standard validator call is enough. $publicKeyCredentialSource = $this->authenticatorAssertionResponseValidator->check( publicKeyCredentialSource: $this->findCredentialSource($publicKeyCredential->id), authenticatorAssertionResponse: $publicKeyCredential->response, publicKeyCredentialRequestOptions: $this->getStoredOptions(), host: 'example.com', userHandle: $this->getCurrentUserId(), ); // Process payment $this->processPayment($publicKeyCredentialSource); return new JsonResponse(['verified' => true]); } } ``` -------------------------------- ### PaymentCurrencyAmount Instantiation with Readonly Properties Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/docs/secure-payment-confirmation.md Creates a transaction amount with ISO 4217 currency and string value; demonstrates readonly property access. ```php use Webauthn\SecurePaymentConfirmation\PaymentCurrencyAmount; $amount = PaymentCurrencyAmount::create( currency: 'USD', // ISO 4217 currency code value: '99.99' // Amount as string ); // Properties are readonly echo $amount->currency; // 'USD' echo $amount->value; // '99.99' ``` -------------------------------- ### Use signal API helpers (dispatchUnknownCredential, dispatchSignals) Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md Import the signal helpers from '@web-auth/webauthn-stimulus' and use dispatchUnknownCredential for a one-shot signal or dispatchSignals for a server-produced envelope. The Authentication/Registration controllers call dispatchSignals automatically on the success response of the resultUrl endpoint, so manual invocation is usually unnecessary. ```javascript import { dispatchUnknownCredential, dispatchAllAcceptedCredentials, dispatchCurrentUserDetails, dispatchSignals, } from '@web-auth/webauthn-stimulus'; // One-shot dispatch for a single signal: await dispatchUnknownCredential({ rpId: 'example.com', credentialId: 'aabbcc' }); // Or dispatch a `signals: [{type, options}]` envelope produced server-side // by `Webauthn\Bundle\Service\WebauthnSignalResponse::withSignals(...)`. // The Authentication / Registration controllers call this automatically on // the success response of the `resultUrl` endpoint, so you usually do not // need to invoke it yourself. await dispatchSignals(serverResponse); ``` -------------------------------- ### Webauthn registration form with Stimulus controller Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/doc/index.rst Twig template for Webauthn registration. Uses the Stimulus controller '@web-auth/webauthn-stimulus/webauthn' and triggers the 'signup' action on submit. Includes username and displayName fields. ```twig
``` -------------------------------- ### Payment form with webauthn--payment controller Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/PAYMENT_CONTROLLER.md HTML form that uses the webauthn--payment controller. The options URL must return a JSON PublicKeyCredentialRequestOptions with extensions.payment set to the serialised PaymentExtension::authenticate(...) output. ```html

Amount: getFormattedAmount()) ?>

``` -------------------------------- ### Listen for webauthn:authentication:credential event Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/PAYMENT_CONTROLLER.md Listen for the authentication credential event. The signature is already base64url encoded, so it can be sent directly to the verifier. ```javascript form.addEventListener('webauthn:authentication:credential', (event) => { // event.detail.credential.clientExtensionResults.payment.browserBoundSignature.signature // is already a base64url string — ready to be sent to the verifier. }); ``` -------------------------------- ### Configure Stimulus Payment Controller in HTML Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/docs/secure-payment-confirmation.md Use the webauthn--payment controller on a form to handle payment confirmation. The transaction-id-value must be a secure identifier, as payment details are fetched server-side to prevent tampering. ```html

Confirm Payment

Amount: getFormattedAmount()) ?>

Merchant: getPayeeName()) ?>

Payment Method: getInstrumentDisplay()) ?>

``` -------------------------------- ### PaymentCredentialInstrument Creation Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/docs/secure-payment-confirmation.md Creates a payment instrument with display name, icon URL, and optional iconMustBeShown flag (default true). ```php use Webauthn\SecurePaymentConfirmation\PaymentCredentialInstrument; $instrument = PaymentCredentialInstrument::create( displayName: 'Visa •••• 1234', icon: 'https://example.com/visa-icon.png', iconMustBeShown: true // Optional, defaults to true ); ``` -------------------------------- ### Register Stimulus controllers with module bundler Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md Register the authentication and registration controllers with a Stimulus application when using a module bundler like webpack, Vite, or esbuild. ```javascript import { Application } from '@hotwired/stimulus'; import { AuthenticationController, RegistrationController } from '@web-auth/webauthn-stimulus'; const app = Application.start(); app.register('webauthn--authentication', AuthenticationController); app.register('webauthn--registration', RegistrationController); ``` -------------------------------- ### Secure Payment Confirmation Form (HTML + Stimulus) Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/docs/secure-payment-confirmation.md Use this HTML form to confirm a payment with WebAuthn. It includes a Stimulus controller that submits to the payment options and verification endpoints. The form displays order and payment method details from server-side data, not from HTML attributes. It also includes event listeners for payment errors and verification success. ```html Secure Payment

Checkout

find($transactionId); ?>

Order Summary

Total: getFormattedAmount()) ?>

Merchant: getPayeeName()) ?>

Payment Method

<?= htmlspecialchars($transaction->getPaymentMethod()->getBrand()) ?> getPaymentMethod()->getDisplayName()) ?>

🔒 This payment will be authenticated using your device's biometric sensor

``` -------------------------------- ### Webauthn login form with Stimulus controller Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/doc/index.rst Twig template for Webauthn login. Uses the same Stimulus controller but triggers the 'signin' action on submit. Only requires a username field. ```twig
``` -------------------------------- ### Import WebauthnController Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md JavaScript import statement for the WebauthnController named export from the @web-auth/webauthn-stimulus package. This is the recommended way to import the controller, replacing the deprecated default import. ```javascript import { WebauthnController } from '@web-auth/webauthn-stimulus'; ``` -------------------------------- ### Handle WebAuthn assertion events Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md Listen for WebAuthn assertion success and failure events. The success event provides the event detail, while the failure event includes an exception object in the detail. ```javascript document.addEventListener('webauthn:assertion:success', (event) => { console.log('Authentication successful!', event.detail); }); document.addEventListener('webauthn:assertion:failure', (event) => { console.error('Authentication failed:', event.detail.exception); }); ``` -------------------------------- ### PHP Development Commands via Castor Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/CLAUDE.md Use these Castor commands for PHP development tasks: running tests, static analysis, code style, refactoring, architecture validation, syntax checking, mutation testing, license compliance, and preparing a PR. Each command is executed from the project root. ```bash # Run all tests with coverage castor phpunit # Static analysis castor phpstan castor phpstan-baseline # Generate new baseline # Code style castor ecs # Check coding standards castor ecs-fix # Auto-fix coding standards # Refactoring castor rector # Check refactoring suggestions castor rector-fix # Apply refactoring # Architecture validation castor deptrac # Check dependency layers # Syntax checking castor lint # Mutation testing castor infect # License compliance castor check-licenses # Prepare code for PR (fixes style, applies Rector, runs analysis) castor prepare-pr ``` -------------------------------- ### Check browser support for SPC Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/docs/secure-payment-confirmation.md Checks if the browser supports WebAuthn, which is required for SPC. Use this to conditionally enable SPC features. ```javascript import { browserSupportsWebAuthn } from '@simplewebauthn/browser'; if (browserSupportsWebAuthn()) { // SPC is supported } ``` -------------------------------- ### Registration Controller Form Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md HTML form using the webauthn--registration Stimulus controller for new credential registration. Includes fields for username, display name, and attestation level, and triggers the register action on submit. ```html
``` -------------------------------- ### Legacy Controller Form Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md HTML form using the deprecated webauthn Stimulus controller for backward compatibility. Provides buttons for sign-up and sign-in actions. ```html
``` -------------------------------- ### WebAuthn registration form with form submission Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md Form for WebAuthn registration with form submission. Uses options URL and submit-via-form flag, with a hidden result target. ```html
``` -------------------------------- ### CollectedClientAdditionalPaymentData Creation Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/docs/secure-payment-confirmation.md Creates additional payment data including rpId, topOrigin, total, instrument, and payee details. ```php use Webauthn\SecurePaymentConfirmation\CollectedClientAdditionalPaymentData; $additionalData = CollectedClientAdditionalPaymentData::create( rpId: 'example.com', topOrigin: 'https://merchant.example.com', total: $amount, instrument: $instrument, payeeName: 'Merchant Store', payeeOrigin: 'https://merchant.example.com', ); ``` -------------------------------- ### Check WebAuthn browser support with browserSupportsWebAuthn Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md Checks if the current browser supports WebAuthn before showing WebAuthn UI. Requires a browser with WebAuthn support (Chrome/Edge 67+, Firefox 60+, Safari 13+, Opera 54+). ```javascript import { browserSupportsWebAuthn } from '@simplewebauthn/browser'; if (browserSupportsWebAuthn()) { // Show WebAuthn UI } ``` -------------------------------- ### Authentication Controller Form Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md HTML form using the webauthn--authentication Stimulus controller for user sign-in. Configured with options and result URLs, enables conditional UI for browser autofill, and triggers the authenticate action on submit. ```html
``` -------------------------------- ### CollectedClientPaymentData Wrapper Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/docs/secure-payment-confirmation.md Wraps additional payment data into the top-level CollectedClientPaymentData object. ```php use Webauthn\SecurePaymentConfirmation\CollectedClientPaymentData; $clientPaymentData = CollectedClientPaymentData::create($additionalData); ``` -------------------------------- ### WebAuthn authentication form with API verification Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md Form for WebAuthn authentication with API verification. Configure options URL, result URL, and success redirect URI via data attributes. ```html
``` -------------------------------- ### Register Stimulus controllers in JavaScript Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md Registers the Stimulus controllers in your JavaScript bootstrap (typically assets/bootstrap.js). Use the same '--'-separated identifiers that stimulus_controller('@web-auth/webauthn-stimulus/...') produces so existing Twig helpers keep working unchanged. ```javascript import { Application } from '@hotwired/stimulus'; import { AuthenticationController, RegistrationController, WebauthnController } from '@web-auth/webauthn-stimulus'; const app = Application.start(); app.register('web-auth--webauthn-stimulus--authentication', AuthenticationController); app.register('web-auth--webauthn-stimulus--registration', RegistrationController); app.register('web-auth--webauthn-stimulus', WebauthnController); ``` -------------------------------- ### WebAuthn authentication form with browser autofill Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/assets/README.md Form for WebAuthn authentication with browser autofill (conditional UI). The username input uses autocomplete="username webauthn" and a target attribute. ```html
``` -------------------------------- ### Listen for Stimulus Payment Controller Events Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/docs/secure-payment-confirmation.md The payment controller dispatches custom events throughout the payment lifecycle, including connection, options retrieval, credential creation, and verification status. ```javascript // Connection event document.addEventListener('webauthn:payment:connect', (event) => { console.log('Payment controller connected'); console.log('Transaction ID:', event.detail.transactionId); }); // Options request/response events document.addEventListener('webauthn:payment:options:request', (event) => { console.log('Requesting payment options for transaction:', event.detail.data.transactionId); }); document.addEventListener('webauthn:payment:options:success', (event) => { console.log('Payment options received', event.detail.options); }); document.addEventListener('webauthn:payment:options:error', (event) => { console.error('Failed to get payment options', event.detail.error); }); // Credential received event document.addEventListener('webauthn:payment:credential', (event) => { console.log('Payment credential created', event.detail.credential); }); // Verification events document.addEventListener('webauthn:payment:verify:request', (event) => { console.log('Verifying payment credential', event.detail.credential); }); document.addEventListener('webauthn:payment:verify:success', (event) => { console.log('Payment verified successfully', event.detail.result); }); document.addEventListener('webauthn:payment:verify:error', (event) => { console.error('Payment verification failed', event.detail.error); }); // Error event document.addEventListener('webauthn:payment:error', (event) => { console.error('Payment error', event.detail.error); if (event.detail.code) { console.error('Error code:', event.detail.code); } }); // Unsupported browser document.addEventListener('webauthn:unsupported', () => { alert('Your browser does not support WebAuthn'); }); ``` -------------------------------- ### Secure transaction flow with transaction ID Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/docs/secure-payment-confirmation.md Secure transaction flow: create a transaction with a cryptographically secure ID, render only the transaction ID, and fetch the transaction server-side for the payment extension. Never trust client-side data. ```php // 1. Create transaction in database (server-side only) $transaction = new Transaction(); $transaction->setId(bin2hex(random_bytes(16))); // Secure ID $transaction->setUserId($currentUser->getId()); $transaction->setAmount('99.99'); $transaction->setCurrency('USD'); $transaction->setPayeeName('Merchant Store'); $transaction->setStatus('pending'); $transaction->setExpiresAt(new DateTime('+15 minutes')); $entityManager->persist($transaction); $entityManager->flush(); // 2. Render page with transaction ID only echo '
'; // 3. In options endpoint: Fetch from database $transaction = $repository->findOneBy([ 'id' => $transactionId, 'userId' => $currentUser->getId(), 'status' => 'pending' ]); if (!$transaction || $transaction->isExpired()) { return new JsonResponse(['error' => 'Invalid transaction'], 400); } // Use $transaction data for payment extension (NOT client data!) ``` -------------------------------- ### Validate SPC with AuthenticatorAssertionResponseValidator Source: https://github.com/web-auth/webauthn-framework/blob/5.4.x/src/stimulus/PAYMENT_CONTROLLER.md Perform full SPC validation with the standard AuthenticatorAssertionResponseValidator->check() call. The Symfony bundle automatically registers PaymentClientDataCollector and PaymentExtensionOutputChecker to validate the payment extension. ```php $publicKeyCredentialSource = $this->authenticatorAssertionResponseValidator->check( publicKeyCredentialSource: $credentialSource, authenticatorAssertionResponse: $credential->response, publicKeyCredentialRequestOptions: $storedOptions, host: 'example.com', userHandle: $userId, ); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.