### 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 ``` -------------------------------- ### 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 ``` -------------------------------- ### 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