This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a FIDO2/WebAuthn framework for PHP and Symfony, implementing the W3C WebAuthn specification. The repository contains three complementary modules organized as a monorepo:
- webauthn-lib (
src/webauthn/) - Core PHP library with no framework dependencies - webauthn-symfony-bundle (
src/symfony/) - Symfony integration providing controllers, services, and configuration - webauthn-stimulus (
src/stimulus/) - Frontend Stimulus.js integration for client-side WebAuthn interactions
The project uses Castor for task automation. Commands use the castor CLI:
# 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# 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 formattingPHPUnit tests are located in:
tests/framework/- Core library teststests/library/- Additional library teststests/symfony/functional/- Symfony bundle functional teststests/MDS/- Metadata Service tests
To run specific tests, use the standard PHPUnit filter syntax via the vendor binary:
vendor/bin/phpunit --filter=TestClassName tests/
vendor/bin/phpunit tests/framework/SomeSpecificTest.phpThe core validation logic uses a "ceremony steps" pattern where both registration and authentication ceremonies are composed of discrete, reusable validation steps:
- Steps are located in
src/webauthn/src/CeremonyStep/ - Each step implements the
CeremonyStepinterface - Steps are orchestrated by
CeremonyStepManager(built viaCeremonyStepManagerFactory) - Registration ceremony includes ~14 steps (CheckOrigin, CheckChallenge, CheckRelyingPartyIdHash, etc.)
- Authentication ceremony includes ~12 steps (CheckSignature, CheckCounter, CheckUserHandle, etc.)
Two primary validators orchestrate the ceremony flows:
AuthenticatorAttestationResponseValidator- Handles registration/attestationAuthenticatorAssertionResponseValidator- Handles authentication/assertion
Both validators:
- Use
CeremonyStepManagerto run validation steps - Support PSR-14 event dispatching for extensibility
- Support PSR-3 logging
- Update credential records with final authenticator data after successful validation
Data persistence is abstracted through repository interfaces:
CredentialRecordRepositoryInterface- Query credentials by user or credential ID- Optional
CanSaveCredentialRecordinterface for persistence operations
- Optional
PublicKeyCredentialUserEntityRepositoryInterface- User lookup by username or user handle- Implementations:
DoctrineCredentialSourceRepository(Doctrine),DummyCredentialRecordRepository(testing)
Key entities that represent WebAuthn protocol data:
Options (sent to client):
PublicKeyCredentialCreationOptions- Registration ceremony optionsPublicKeyCredentialRequestOptions- Authentication ceremony options
Responses (received from client):
AuthenticatorAttestationResponse- Registration response with attestationObjectAuthenticatorAssertionResponse- Authentication response with signature
Stored Credentials:
CredentialRecord- Server-side representation of a credential (credential ID, public key, counter, backup status, etc.)PublicKeyCredentialUserEntity- User informationPublicKeyCredentialRpEntity- Relying Party information
Supporting Data:
CollectedClientData- Parsed clientDataJSON (challenge, origin, type)AuthenticatorData- Parsed authenticator data (RP ID hash, flags, counter, extensions)AttestationObject- Contains attestation statement and authenticator data
Registration (Attestation):
- Client calls
/attestation/request→ server generatesPublicKeyCredentialCreationOptions - Options stored in session/cache, returned to client
- Client uses WebAuthn API to create credential
- Client posts response to
/attestation/response - Server validates via
AuthenticatorAttestationResponseValidator.check()using ceremony steps - If valid, credential persisted via repository
Authentication (Assertion):
- Client calls
/assertion/request→ server generatesPublicKeyCredentialRequestOptions - Options stored in session/cache, returned to client
- Client uses WebAuthn API to get credential
- Client posts response to
/assertion/response - Server validates via
AuthenticatorAssertionResponseValidator.check()using ceremony steps - If valid, credential record updated with new counter and backup status
Located in src/symfony/:
Controllers:
AttestationRequestController,AttestationResponseController- Handle registrationAssertionRequestController,AssertionResponseController- Handle authentication- Controllers use factory pattern (
AttestationControllerFactory,AssertionControllerFactory)
Configuration:
- Service definitions in
src/symfony/src/Resources/config/ - Doctrine mappings in
src/symfony/src/Resources/config/doctrine-mapping/ - Bundle configuration via
Configuration.php
Security Integration:
WebauthnAuthenticator- Symfony security authenticatorWebauthnToken- Authentication token- Event system for security events
Options Building:
PublicKeyCredentialCreationOptionsBuilder- Interface for building creation optionsProfileBasedCreationOptionsBuilder- Profile-driven implementationPublicKeyCredentialCreationOptionsFactory- Factory with configured settings
Success/Failure Handlers:
- Provide extensible hooks for custom post-validation logic
SuccessHandler/FailureHandlerinterfaces with default implementations- Used by controllers to handle ceremony outcomes
The codebase recently underwent a significant refactoring (visible in recent commits):
PublicKeyCredentialSourcerenamed toCredentialRecordPublicKeyCredentialSourceRepositoryrenamed toCredentialRecordRepository- This improves clarity about what the entity represents (a record of a credential, not the credential itself)
- PHP >= 8.2
- Extensions: json, openssl
- Symfony 6.4+ or 7.0+
- Tests are in
tests/directory with subdirectories for each module - PHPUnit configuration at
.ci-tools/phpunit.xml.dist - Mutation testing via Infection for quality assurance
- Functional tests for Symfony bundle integration
- JavaScript tests using Jest
GitHub Actions workflow (.github/workflows/ci.yml) runs:
- Pre-checks (file permissions, ASCII characters)
- PHPStan (static analysis)
- ECS (coding standards)
- Rector (refactoring checks)
- Composer validation
- Syntax checking
- License compliance
- Deptrac (architecture validation)
- PHPUnit tests (PHP 8.2, 8.3, 8.4)
- JavaScript lint, format, and tests
- Mutation testing (on release branches)
Full documentation available at: https://webauthn-doc.spomky-labs.com/
- Main branch for PRs:
5.3.x(or current version branch, notmain) - This is a monorepo that replaces separate packages via
composer.jsonreplacesection - Security vulnerabilities should be reported via GitHub Security Advisory, not public issues
- Recent refactoring renamed
PublicKeyCredentialSourcetoCredentialRecord- be aware when working with older code or documentation