From 545ea187a926266f65d831f6b72009f7da2a1da6 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sat, 18 Nov 2023 10:40:02 +0100 Subject: [PATCH 1/6] Remove useless CBOR Bundle (only lib us used) --- composer.json | 1 - src/symfony/composer.json | 1 - tests/symfony/functional/AppKernel.php | 2 -- 3 files changed, 4 deletions(-) diff --git a/composer.json b/composer.json index c761119ac..1c50aa4d2 100644 --- a/composer.json +++ b/composer.json @@ -54,7 +54,6 @@ "psr/http-client": "^1.0", "psr/http-factory": "^1.0", "psr/log": "^1.0|^2.0|^3.0", - "spomky-labs/cbor-bundle": "^3.0", "spomky-labs/cbor-php": "^3.0", "spomky-labs/pki-framework": "^1.0", "symfony/config": "^6.1", diff --git a/src/symfony/composer.json b/src/symfony/composer.json index ba8906834..916a32b94 100644 --- a/src/symfony/composer.json +++ b/src/symfony/composer.json @@ -26,7 +26,6 @@ "php": ">=8.1", "nyholm/psr7": "^1.5", "psr/event-dispatcher": "^1.0", - "spomky-labs/cbor-bundle": "^3.0", "symfony/config": "^6.1", "symfony/dependency-injection": "^6.1", "symfony/framework-bundle": "^6.1", diff --git a/tests/symfony/functional/AppKernel.php b/tests/symfony/functional/AppKernel.php index ab42baa04..b0ab6a2ae 100644 --- a/tests/symfony/functional/AppKernel.php +++ b/tests/symfony/functional/AppKernel.php @@ -5,7 +5,6 @@ namespace Webauthn\Tests\Bundle\Functional; use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; -use SpomkyLabs\CborBundle\SpomkyLabsCborBundle; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Bundle\MonologBundle\MonologBundle; use Symfony\Bundle\SecurityBundle\SecurityBundle; @@ -25,7 +24,6 @@ public function registerBundles(): iterable { return [ new FrameworkBundle(), - new SpomkyLabsCborBundle(), new DoctrineBundle(), new SecurityBundle(), new MonologBundle(), From b2c981b704e36fa81e23a2133abed9b3f3215780 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sun, 19 Nov 2023 11:02:03 +0100 Subject: [PATCH 2/6] Enable XSD validation for Doctrine ORM 3.0 compat --- .../src/DependencyInjection/WebauthnStimulusExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stimulus/src/DependencyInjection/WebauthnStimulusExtension.php b/src/stimulus/src/DependencyInjection/WebauthnStimulusExtension.php index e86ce7499..23f07887c 100644 --- a/src/stimulus/src/DependencyInjection/WebauthnStimulusExtension.php +++ b/src/stimulus/src/DependencyInjection/WebauthnStimulusExtension.php @@ -19,7 +19,7 @@ public function load(array $configs, ContainerBuilder $container) // Nothing to do here. The bundle is only used to provide assets. } - public function prepend(ContainerBuilder $container) + public function prepend(ContainerBuilder $container): void { if (! $this->isAssetMapperAvailable($container)) { return; From 455c9a50faed333457fe2b056b78af847d96b943 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sun, 19 Nov 2023 11:07:12 +0100 Subject: [PATCH 3/6] Enable XSD validation for Doctrine ORM 3.0 compat --- src/symfony/src/WebauthnBundle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/symfony/src/WebauthnBundle.php b/src/symfony/src/WebauthnBundle.php index f6597227b..91d3e0914 100644 --- a/src/symfony/src/WebauthnBundle.php +++ b/src/symfony/src/WebauthnBundle.php @@ -88,7 +88,7 @@ private function registerMappings(ContainerBuilder $container): void ]; if (class_exists(DoctrineOrmMappingsPass::class)) { $container->addCompilerPass( - DoctrineOrmMappingsPass::createXmlMappingDriver($mappings, []), + DoctrineOrmMappingsPass::createXmlMappingDriver($mappings, [], false, [], true), PassConfig::TYPE_BEFORE_OPTIMIZATION, 0 ); From 04974e61139231e272fa962976f95b6814d9fa87 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sun, 19 Nov 2023 11:08:03 +0100 Subject: [PATCH 4/6] Enable XSD validation for Doctrine ORM 3.0 compat --- .../src/DependencyInjection/WebauthnStimulusExtension.php | 2 +- src/symfony/src/DependencyInjection/WebauthnExtension.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stimulus/src/DependencyInjection/WebauthnStimulusExtension.php b/src/stimulus/src/DependencyInjection/WebauthnStimulusExtension.php index 23f07887c..28b657225 100644 --- a/src/stimulus/src/DependencyInjection/WebauthnStimulusExtension.php +++ b/src/stimulus/src/DependencyInjection/WebauthnStimulusExtension.php @@ -14,7 +14,7 @@ */ class WebauthnStimulusExtension extends Extension implements PrependExtensionInterface { - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { // Nothing to do here. The bundle is only used to provide assets. } diff --git a/src/symfony/src/DependencyInjection/WebauthnExtension.php b/src/symfony/src/DependencyInjection/WebauthnExtension.php index 36a533afd..b41e96a1b 100644 --- a/src/symfony/src/DependencyInjection/WebauthnExtension.php +++ b/src/symfony/src/DependencyInjection/WebauthnExtension.php @@ -63,7 +63,7 @@ public function getAlias(): string return $this->alias; } - public function load(array $configs, ContainerBuilder $container): void + public function load(array $configs, ContainerBuilder $container) { $processor = new Processor(); $config = $processor->processConfiguration( From ac29e0c368dbf34d446e277b84c9769fe2bc9337 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sun, 19 Nov 2023 11:11:37 +0100 Subject: [PATCH 5/6] - --- src/symfony/src/DependencyInjection/WebauthnExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/symfony/src/DependencyInjection/WebauthnExtension.php b/src/symfony/src/DependencyInjection/WebauthnExtension.php index b41e96a1b..36a533afd 100644 --- a/src/symfony/src/DependencyInjection/WebauthnExtension.php +++ b/src/symfony/src/DependencyInjection/WebauthnExtension.php @@ -63,7 +63,7 @@ public function getAlias(): string return $this->alias; } - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $processor = new Processor(); $config = $processor->processConfiguration( From 450d9640912a89a2db63debdce5bb0416cb3a2e2 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sun, 19 Nov 2023 21:17:00 +0100 Subject: [PATCH 6/6] Comments addressed (#531) Comments addressed --- phpstan-baseline.neon | 70 +++++++++++++- .../src/Service/MetadataBLOBPayloadEntry.php | 1 - .../src/DataCollector/WebauthnCollector.php | 46 +++++----- ...AssertionResponseValidationFailedEvent.php | 3 + ...ertionResponseValidationSucceededEvent.php | 3 + ...testationResponseValidationFailedEvent.php | 3 + ...tationResponseValidationSucceededEvent.php | 3 + ...yCredentialCreationOptionsCreatedEvent.php | 5 +- ...eyCredentialRequestOptionsCreatedEvent.php | 5 +- src/symfony/src/Resources/config/services.php | 79 +++++++++++++--- ...idSafetyNetAttestationStatementSupport.php | 4 +- ...uthenticatorAssertionResponseValidator.php | 15 +-- .../src/AuthenticatorSelectionCriteria.php | 28 ++++-- .../CeremonyStepManagerFactory.php | 4 +- .../src/CeremonyStep/CheckExtensions.php | 2 +- .../AttestationObjectDenormalizer.php | 2 +- .../AttestationStatementDenormalizer.php | 2 +- .../AuthenticationExtensionsDenormalizer.php | 2 +- ...enticatorAssertionResponseDenormalizer.php | 2 +- ...ticatorAttestationResponseDenormalizer.php | 2 +- .../AuthenticatorDataDenormalizer.php | 2 +- .../AuthenticatorResponseDenormalizer.php | 2 +- .../CollectedClientDataDenormalizer.php | 2 +- .../PublicKeyCredentialDenormalizer.php | 2 +- ...PublicKeyCredentialOptionsDenormalizer.php | 2 +- .../PublicKeyCredentialSourceDenormalizer.php | 2 +- ...licKeyCredentialUserEntityDenormalizer.php | 2 +- .../Denormalizer/TrustPathDenormalizer.php | 28 ++---- ...AssertionResponseValidationFailedEvent.php | 9 ++ ...ertionResponseValidationSucceededEvent.php | 25 ++++- ...testationResponseValidationFailedEvent.php | 15 ++- ...tationResponseValidationSucceededEvent.php | 15 ++- ...droidSafetyNetAttestationStatementTest.php | 2 +- .../AppleAttestationStatementTest.php | 4 +- ...testationStatementWithTokenBindingTest.php | 2 +- .../Fido2AttestationStatementTest.php | 2 +- .../AuthenticatorSelectionCriteriaTest.php | 9 +- .../AdditionalAuthenticatorTest.php | 55 +++++------ .../Firewall/RegistrationAreaTest.php | 91 ++++++++++--------- .../functional/Firewall/SecuredAreaTest.php | 50 +++++----- 40 files changed, 389 insertions(+), 213 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 1443e54a0..af345b0ab 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -794,6 +794,54 @@ parameters: count: 1 path: src/symfony/src/CredentialOptionsBuilder/ProfileBasedRequestOptionsBuilder.php + - + message: """ + #^Fetching class constant class of deprecated class Webauthn\\\\Bundle\\\\Event\\\\AuthenticatorAssertionResponseValidationFailedEvent\\: + since 4\\.3\\.0, use \\{@link \\\\Webauthn\\\\Event\\\\AuthenticatorAssertionResponseValidationFailedEvent\\} instead\\.$# + """ + count: 1 + path: src/symfony/src/DataCollector/WebauthnCollector.php + + - + message: """ + #^Fetching class constant class of deprecated class Webauthn\\\\Bundle\\\\Event\\\\AuthenticatorAssertionResponseValidationSucceededEvent\\: + since 4\\.3\\.0, use \\{@link \\\\Webauthn\\\\Event\\\\AuthenticatorAssertionResponseValidationSucceededEvent\\} instead\\.$# + """ + count: 1 + path: src/symfony/src/DataCollector/WebauthnCollector.php + + - + message: """ + #^Fetching class constant class of deprecated class Webauthn\\\\Bundle\\\\Event\\\\AuthenticatorAttestationResponseValidationSucceededEvent\\: + since 4\\.3\\.0, use \\{@link \\\\Webauthn\\\\Event\\\\AuthenticatorAttestationResponseValidationSucceededEvent\\} instead\\.$# + """ + count: 1 + path: src/symfony/src/DataCollector/WebauthnCollector.php + + - + message: """ + #^Parameter \\$event of method Webauthn\\\\Bundle\\\\DataCollector\\\\WebauthnCollector\\:\\:addAuthenticatorAssertionResponseValidationFailed\\(\\) has typehint with deprecated class Webauthn\\\\Bundle\\\\Event\\\\AuthenticatorAssertionResponseValidationFailedEvent\\: + since 4\\.3\\.0, use \\{@link \\\\Webauthn\\\\Event\\\\AuthenticatorAssertionResponseValidationFailedEvent\\} instead\\.$# + """ + count: 1 + path: src/symfony/src/DataCollector/WebauthnCollector.php + + - + message: """ + #^Parameter \\$event of method Webauthn\\\\Bundle\\\\DataCollector\\\\WebauthnCollector\\:\\:addAuthenticatorAssertionResponseValidationSucceeded\\(\\) has typehint with deprecated class Webauthn\\\\Bundle\\\\Event\\\\AuthenticatorAssertionResponseValidationSucceededEvent\\: + since 4\\.3\\.0, use \\{@link \\\\Webauthn\\\\Event\\\\AuthenticatorAssertionResponseValidationSucceededEvent\\} instead\\.$# + """ + count: 1 + path: src/symfony/src/DataCollector/WebauthnCollector.php + + - + message: """ + #^Parameter \\$event of method Webauthn\\\\Bundle\\\\DataCollector\\\\WebauthnCollector\\:\\:addAuthenticatorAttestationResponseValidationSucceeded\\(\\) has typehint with deprecated class Webauthn\\\\Bundle\\\\Event\\\\AuthenticatorAttestationResponseValidationSucceededEvent\\: + since 4\\.3\\.0, use \\{@link \\\\Webauthn\\\\Event\\\\AuthenticatorAttestationResponseValidationSucceededEvent\\} instead\\.$# + """ + count: 1 + path: src/symfony/src/DataCollector/WebauthnCollector.php + - message: "#^Cannot call method integerNode\\(\\) on Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\|null\\.$#" count: 2 @@ -2169,7 +2217,7 @@ parameters: #^Access to deprecated property \\$requireResidentKey of class Webauthn\\\\AuthenticatorSelectionCriteria\\: Will be removed in 5\\.0\\. Please use residentKey instead\\*$# """ - count: 2 + count: 3 path: src/webauthn/src/AuthenticatorSelectionCriteria.php - @@ -2887,6 +2935,19 @@ parameters: count: 1 path: src/webauthn/src/Denormalizer/PublicKeyCredentialUserEntityDenormalizer.php + - + message: "#^Cannot access offset 'type' on mixed\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/TrustPathDenormalizer.php + + - + message: """ + #^Instantiation of deprecated class Webauthn\\\\TrustPath\\\\EcdaaKeyIdTrustPath\\: + since 4\\.2\\.0 and will be removed in 5\\.0\\.0\\. The ECDAA Trust Anchor does no longer exist in Webauthn specification\\.$# + """ + count: 1 + path: src/webauthn/src/Denormalizer/TrustPathDenormalizer.php + - message: "#^Method Webauthn\\\\Denormalizer\\\\TrustPathDenormalizer\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 @@ -2898,10 +2959,15 @@ parameters: path: src/webauthn/src/Denormalizer/TrustPathDenormalizer.php - - message: "#^Parameter \\#2 \\$array of function array_key_exists expects array, mixed given\\.$#" + message: "#^Parameter \\#1 \\$ecdaaKeyId of class Webauthn\\\\TrustPath\\\\EcdaaKeyIdTrustPath constructor expects string, array given\\.$#" count: 1 path: src/webauthn/src/Denormalizer/TrustPathDenormalizer.php + - + message: "#^Parameter \\#2 \\$array of function array_key_exists expects array, mixed given\\.$#" + count: 2 + path: src/webauthn/src/Denormalizer/TrustPathDenormalizer.php + - message: "#^Parameter \\#1 \\$json of static method Webauthn\\\\PublicKeyCredentialCreationOptions\\:\\:createFromArray\\(\\) expects array, mixed given\\.$#" count: 1 diff --git a/src/metadata-service/src/Service/MetadataBLOBPayloadEntry.php b/src/metadata-service/src/Service/MetadataBLOBPayloadEntry.php index 575129a21..f1d9011a7 100644 --- a/src/metadata-service/src/Service/MetadataBLOBPayloadEntry.php +++ b/src/metadata-service/src/Service/MetadataBLOBPayloadEntry.php @@ -54,7 +54,6 @@ public function __construct( 'Invalid attestation certificate identifier. Shall be a list of strings' ); } - $this->attestationCertificateKeyIdentifiers = $attestationCertificateKeyIdentifiers; } /** diff --git a/src/symfony/src/DataCollector/WebauthnCollector.php b/src/symfony/src/DataCollector/WebauthnCollector.php index 322fe335e..3f4d3d73a 100644 --- a/src/symfony/src/DataCollector/WebauthnCollector.php +++ b/src/symfony/src/DataCollector/WebauthnCollector.php @@ -109,9 +109,9 @@ public function addPublicKeyCredentialCreationOptions(PublicKeyCredentialCreatio { $cloner = new VarCloner(); $this->publicKeyCredentialCreationOptions[] = [ - 'options' => $cloner->cloneVar($event->getPublicKeyCredentialCreationOptions()), + 'options' => $cloner->cloneVar($event->publicKeyCredentialCreationOptions), 'json' => json_encode( - $event->getPublicKeyCredentialCreationOptions(), + $event->publicKeyCredentialCreationOptions, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT ), ]; @@ -122,13 +122,13 @@ public function addAuthenticatorAttestationResponseValidationSucceeded( ): void { $cloner = new VarCloner(); $this->authenticatorAttestationResponseValidationSucceeded[] = [ - 'attestation_response' => $cloner->cloneVar($event->getAuthenticatorAttestationResponse()), - 'options' => $cloner->cloneVar($event->getPublicKeyCredentialCreationOptions()), + 'attestation_response' => $cloner->cloneVar($event->authenticatorAttestationResponse), + 'options' => $cloner->cloneVar($event->publicKeyCredentialCreationOptions), 'options_json' => json_encode( - $event->getPublicKeyCredentialCreationOptions(), + $event->publicKeyCredentialCreationOptions, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT ), - 'credential_source' => $cloner->cloneVar($event->getPublicKeyCredentialSource()), + 'credential_source' => $cloner->cloneVar($event->publicKeyCredentialSource), ]; } @@ -137,13 +137,13 @@ public function addAuthenticatorAttestationResponseValidationFailed( ): void { $cloner = new VarCloner(); $this->authenticatorAttestationResponseValidationFailed[] = [ - 'attestation_response' => $cloner->cloneVar($event->getAuthenticatorAttestationResponse()), - 'options' => $cloner->cloneVar($event->getPublicKeyCredentialCreationOptions()), + 'attestation_response' => $cloner->cloneVar($event->authenticatorAttestationResponse), + 'options' => $cloner->cloneVar($event->publicKeyCredentialCreationOptions), 'options_json' => json_encode( - $event->getPublicKeyCredentialCreationOptions(), + $event->publicKeyCredentialCreationOptions, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT ), - 'exception' => $cloner->cloneVar($event->getThrowable()), + 'exception' => $cloner->cloneVar($event->throwable), ]; } @@ -151,9 +151,9 @@ public function addPublicKeyCredentialRequestOptions(PublicKeyCredentialRequestO { $cloner = new VarCloner(); $this->publicKeyCredentialRequestOptions[] = [ - 'options' => $cloner->cloneVar($event->getPublicKeyCredentialRequestOptions()), + 'options' => $cloner->cloneVar($event->publicKeyCredentialRequestOptions), 'json' => json_encode( - $event->getPublicKeyCredentialRequestOptions(), + $event->publicKeyCredentialRequestOptions, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT ), ]; @@ -164,15 +164,15 @@ public function addAuthenticatorAssertionResponseValidationSucceeded( ): void { $cloner = new VarCloner(); $this->authenticatorAssertionResponseValidationSucceeded[] = [ - 'user_handle' => $cloner->cloneVar($event->getUserHandle()), - 'credential_id' => $cloner->cloneVar($event->getCredentialId()), - 'assertion_response' => $cloner->cloneVar($event->getAuthenticatorAssertionResponse()), - 'options' => $cloner->cloneVar($event->getPublicKeyCredentialRequestOptions()), + 'user_handle' => $cloner->cloneVar($event->userHandle), + 'credential_id' => $cloner->cloneVar($event->credentialId), + 'assertion_response' => $cloner->cloneVar($event->authenticatorAssertionResponse), + 'options' => $cloner->cloneVar($event->publicKeyCredentialRequestOptions), 'options_json' => json_encode( - $event->getPublicKeyCredentialRequestOptions(), + $event->publicKeyCredentialRequestOptions, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT ), - 'credential_source' => $cloner->cloneVar($event->getPublicKeyCredentialSource()), + 'credential_source' => $cloner->cloneVar($event->publicKeyCredentialSource), ]; } @@ -181,15 +181,15 @@ public function addAuthenticatorAssertionResponseValidationFailed( ): void { $cloner = new VarCloner(); $this->authenticatorAssertionResponseValidationFailed[] = [ - 'user_handle' => $cloner->cloneVar($event->getUserHandle()), + 'user_handle' => $cloner->cloneVar($event->userHandle), 'credential_id' => $cloner->cloneVar($event->getCredential()?->publicKeyCredentialId), - 'assertion_response' => $cloner->cloneVar($event->getAuthenticatorAssertionResponse()), - 'options' => $cloner->cloneVar($event->getPublicKeyCredentialRequestOptions()), + 'assertion_response' => $cloner->cloneVar($event->authenticatorAssertionResponse), + 'options' => $cloner->cloneVar($event->publicKeyCredentialRequestOptions), 'options_json' => json_encode( - $event->getPublicKeyCredentialRequestOptions(), + $event->publicKeyCredentialRequestOptions, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT ), - 'exception' => $cloner->cloneVar($event->getThrowable()), + 'exception' => $cloner->cloneVar($event->throwable), ]; } } diff --git a/src/symfony/src/Event/AuthenticatorAssertionResponseValidationFailedEvent.php b/src/symfony/src/Event/AuthenticatorAssertionResponseValidationFailedEvent.php index 2ec0f41c2..b7e5c0dac 100644 --- a/src/symfony/src/Event/AuthenticatorAssertionResponseValidationFailedEvent.php +++ b/src/symfony/src/Event/AuthenticatorAssertionResponseValidationFailedEvent.php @@ -11,6 +11,9 @@ use Webauthn\PublicKeyCredentialRequestOptions; use Webauthn\PublicKeyCredentialSource; +/** + * @deprecated since 4.3.0, use {@link \Webauthn\Event\AuthenticatorAssertionResponseValidationFailedEvent} instead. + */ class AuthenticatorAssertionResponseValidationFailedEvent extends BaseAuthenticatorAssertionResponseValidationFailedEvent { public function __construct( diff --git a/src/symfony/src/Event/AuthenticatorAssertionResponseValidationSucceededEvent.php b/src/symfony/src/Event/AuthenticatorAssertionResponseValidationSucceededEvent.php index b70b33c0a..306522640 100644 --- a/src/symfony/src/Event/AuthenticatorAssertionResponseValidationSucceededEvent.php +++ b/src/symfony/src/Event/AuthenticatorAssertionResponseValidationSucceededEvent.php @@ -10,6 +10,9 @@ use Webauthn\PublicKeyCredentialRequestOptions; use Webauthn\PublicKeyCredentialSource; +/** + * @deprecated since 4.3.0, use {@link \Webauthn\Event\AuthenticatorAssertionResponseValidationSucceededEvent} instead. + */ class AuthenticatorAssertionResponseValidationSucceededEvent extends BaseAuthenticatorAssertionResponseValidationSucceededEvent { public function __construct( diff --git a/src/symfony/src/Event/AuthenticatorAttestationResponseValidationFailedEvent.php b/src/symfony/src/Event/AuthenticatorAttestationResponseValidationFailedEvent.php index fd3ac293d..016f64e5b 100644 --- a/src/symfony/src/Event/AuthenticatorAttestationResponseValidationFailedEvent.php +++ b/src/symfony/src/Event/AuthenticatorAttestationResponseValidationFailedEvent.php @@ -10,6 +10,9 @@ use Webauthn\Event\AuthenticatorAttestationResponseValidationFailedEvent as BaseAuthenticatorAttestationResponseValidationFailedEvent; use Webauthn\PublicKeyCredentialCreationOptions; +/** + * @deprecated since 4.3.0, use {@link \Webauthn\Event\AuthenticatorAttestationResponseValidationFailedEvent} instead. + */ class AuthenticatorAttestationResponseValidationFailedEvent extends BaseAuthenticatorAttestationResponseValidationFailedEvent { public function __construct( diff --git a/src/symfony/src/Event/AuthenticatorAttestationResponseValidationSucceededEvent.php b/src/symfony/src/Event/AuthenticatorAttestationResponseValidationSucceededEvent.php index 93a4605aa..0a1f15712 100644 --- a/src/symfony/src/Event/AuthenticatorAttestationResponseValidationSucceededEvent.php +++ b/src/symfony/src/Event/AuthenticatorAttestationResponseValidationSucceededEvent.php @@ -10,6 +10,9 @@ use Webauthn\PublicKeyCredentialCreationOptions; use Webauthn\PublicKeyCredentialSource; +/** + * @deprecated since 4.3.0, use {@link \Webauthn\Event\AuthenticatorAttestationResponseValidationSucceededEvent} instead. + */ class AuthenticatorAttestationResponseValidationSucceededEvent extends BaseAuthenticatorAttestationResponseValidationSucceededEvent { public function __construct( diff --git a/src/symfony/src/Event/PublicKeyCredentialCreationOptionsCreatedEvent.php b/src/symfony/src/Event/PublicKeyCredentialCreationOptionsCreatedEvent.php index 7995731e4..f4cdef414 100644 --- a/src/symfony/src/Event/PublicKeyCredentialCreationOptionsCreatedEvent.php +++ b/src/symfony/src/Event/PublicKeyCredentialCreationOptionsCreatedEvent.php @@ -10,7 +10,7 @@ class PublicKeyCredentialCreationOptionsCreatedEvent extends Event { public function __construct( - private readonly PublicKeyCredentialCreationOptions $publicKeyCredentialCreationOptions + public readonly PublicKeyCredentialCreationOptions $publicKeyCredentialCreationOptions ) { } @@ -19,6 +19,9 @@ public static function create(PublicKeyCredentialCreationOptions $publicKeyCrede return new self($publicKeyCredentialCreationOptions); } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getPublicKeyCredentialCreationOptions(): PublicKeyCredentialCreationOptions { return $this->publicKeyCredentialCreationOptions; diff --git a/src/symfony/src/Event/PublicKeyCredentialRequestOptionsCreatedEvent.php b/src/symfony/src/Event/PublicKeyCredentialRequestOptionsCreatedEvent.php index 85b53a7ca..80b3794ae 100644 --- a/src/symfony/src/Event/PublicKeyCredentialRequestOptionsCreatedEvent.php +++ b/src/symfony/src/Event/PublicKeyCredentialRequestOptionsCreatedEvent.php @@ -10,7 +10,7 @@ class PublicKeyCredentialRequestOptionsCreatedEvent extends Event { public function __construct( - private readonly PublicKeyCredentialRequestOptions $publicKeyCredentialRequestOptions + public readonly PublicKeyCredentialRequestOptions $publicKeyCredentialRequestOptions ) { } @@ -19,6 +19,9 @@ public static function create(PublicKeyCredentialRequestOptions $publicKeyCreden return new self($publicKeyCredentialRequestOptions); } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getPublicKeyCredentialRequestOptions(): PublicKeyCredentialRequestOptions { return $this->publicKeyCredentialRequestOptions; diff --git a/src/symfony/src/Resources/config/services.php b/src/symfony/src/Resources/config/services.php index 4422ea1fb..ba1398686 100644 --- a/src/symfony/src/Resources/config/services.php +++ b/src/symfony/src/Resources/config/services.php @@ -184,21 +184,72 @@ $container ->alias('webauthn.request_factory.default', RequestFactoryInterface::class); - $container->set(ExtensionDescriptorDenormalizer::class); - $container->set(AttestationObjectDenormalizer::class); - $container->set(AttestationStatementDenormalizer::class) + $container + ->set(ExtensionDescriptorDenormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(AttestationObjectDenormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(AttestationStatementDenormalizer::class) ->args([service(AttestationStatementSupportManager::class)]) - ; - $container->set(AuthenticationExtensionsDenormalizer::class); - $container->set(AuthenticatorAssertionResponseDenormalizer::class); - $container->set(AuthenticatorAttestationResponseDenormalizer::class); - $container->set(AuthenticatorDataDenormalizer::class); - $container->set(AuthenticatorResponseDenormalizer::class); - $container->set(CollectedClientDataDenormalizer::class); - $container->set(PublicKeyCredentialDenormalizer::class); - $container->set(PublicKeyCredentialOptionsDenormalizer::class); - $container->set(PublicKeyCredentialSourceDenormalizer::class); - $container->set(PublicKeyCredentialUserEntityDenormalizer::class); + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(AuthenticationExtensionsDenormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(AuthenticatorAssertionResponseDenormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(AuthenticatorAttestationResponseDenormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(AuthenticatorDataDenormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(AuthenticatorResponseDenormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(CollectedClientDataDenormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(PublicKeyCredentialDenormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(PublicKeyCredentialOptionsDenormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(PublicKeyCredentialSourceDenormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(PublicKeyCredentialUserEntityDenormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); $container->set(WebauthnSerializerFactory::class) ->args([service(AttestationStatementSupportManager::class)]) ; diff --git a/src/webauthn/src/AttestationStatement/AndroidSafetyNetAttestationStatementSupport.php b/src/webauthn/src/AttestationStatement/AndroidSafetyNetAttestationStatementSupport.php index b9ba6a8a7..52f7a872d 100644 --- a/src/webauthn/src/AttestationStatement/AndroidSafetyNetAttestationStatementSupport.php +++ b/src/webauthn/src/AttestationStatement/AndroidSafetyNetAttestationStatementSupport.php @@ -91,9 +91,9 @@ public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): v $this->dispatcher = $eventDispatcher; } - public static function create(): self + public static function create(null|ClockInterface $clock = null): self { - return new self(); + return new self($clock); } public function enableApiVerification( diff --git a/src/webauthn/src/AuthenticatorAssertionResponseValidator.php b/src/webauthn/src/AuthenticatorAssertionResponseValidator.php index 96a065c08..9867c5ef9 100644 --- a/src/webauthn/src/AuthenticatorAssertionResponseValidator.php +++ b/src/webauthn/src/AuthenticatorAssertionResponseValidator.php @@ -33,10 +33,10 @@ class AuthenticatorAssertionResponseValidator implements CanLogData, CanDispatch public function __construct( private readonly null|PublicKeyCredentialSourceRepository $publicKeyCredentialSourceRepository = null, - private readonly null|TokenBindingHandler $tokenBindingHandler = null, - null|ExtensionOutputCheckerHandler $extensionOutputCheckerHandler = null, - null|Manager $algorithmManager = null, - null|EventDispatcherInterface $eventDispatcher = null, + private readonly null|TokenBindingHandler $tokenBindingHandler = null, + null|ExtensionOutputCheckerHandler $extensionOutputCheckerHandler = null, + null|Manager $algorithmManager = null, + null|EventDispatcherInterface $eventDispatcher = null, private null|CeremonyStepManager $ceremonyStepManager = null ) { if ($this->publicKeyCredentialSourceRepository !== null) { @@ -180,6 +180,7 @@ public function check( try { $this->logger->info('Checking the authenticator assertion response', [ 'credentialId' => $credentialId, + 'publicKeyCredentialSource' => $publicKeyCredentialSource, 'authenticatorAssertionResponse' => $authenticatorAssertionResponse, 'publicKeyCredentialRequestOptions' => $publicKeyCredentialRequestOptions, 'host' => $host, @@ -233,7 +234,7 @@ public function check( ]); $this->eventDispatcher->dispatch( $this->createAuthenticatorAssertionResponseValidationFailedEvent( - $credentialId, + $publicKeyCredentialSource, $authenticatorAssertionResponse, $publicKeyCredentialRequestOptions, $host, @@ -294,7 +295,7 @@ protected function createAuthenticatorAssertionResponseValidationSucceededEvent( } protected function createAuthenticatorAssertionResponseValidationFailedEvent( - string|PublicKeyCredentialSource $credentialId, + string|PublicKeyCredentialSource $publicKeyCredentialSource, AuthenticatorAssertionResponse $authenticatorAssertionResponse, PublicKeyCredentialRequestOptions $publicKeyCredentialRequestOptions, ServerRequestInterface|string $host, @@ -313,7 +314,7 @@ protected function createAuthenticatorAssertionResponseValidationFailedEvent( ); } return new AuthenticatorAssertionResponseValidationFailedEvent( - $credentialId, + $publicKeyCredentialSource, $authenticatorAssertionResponse, $publicKeyCredentialRequestOptions, $host, diff --git a/src/webauthn/src/AuthenticatorSelectionCriteria.php b/src/webauthn/src/AuthenticatorSelectionCriteria.php index d0e610f7a..0bbad86fd 100644 --- a/src/webauthn/src/AuthenticatorSelectionCriteria.php +++ b/src/webauthn/src/AuthenticatorSelectionCriteria.php @@ -62,9 +62,9 @@ class AuthenticatorSelectionCriteria implements JsonSerializable public function __construct( public null|string $authenticatorAttachment = null, public string $userVerification = self::USER_VERIFICATION_REQUIREMENT_PREFERRED, - public null|string $residentKey = self::RESIDENT_KEY_REQUIREMENT_PREFERRED, + public null|string $residentKey = self::RESIDENT_KEY_REQUIREMENT_NO_PREFERENCE, /** @deprecated Will be removed in 5.0. Please use residentKey instead**/ - public null|bool $requireResidentKey = false, + public null|bool $requireResidentKey = null, ) { in_array($authenticatorAttachment, self::AUTHENTICATOR_ATTACHMENTS, true) || throw new InvalidArgumentException( 'Invalid authenticator attachment' @@ -75,17 +75,22 @@ public function __construct( in_array($residentKey, self::RESIDENT_KEY_REQUIREMENTS, true) || throw new InvalidArgumentException( 'Invalid resident key' ); - - $this->requireResidentKey = $requireResidentKey ?? $residentKey === self::RESIDENT_KEY_REQUIREMENT_REQUIRED; - $requireResidentKey = $requireResidentKey === true ? self::RESIDENT_KEY_REQUIREMENT_REQUIRED : self::RESIDENT_KEY_REQUIREMENT_PREFERRED; - $this->residentKey = $residentKey ?? $requireResidentKey; + if ($requireResidentKey === true && $residentKey !== null && $residentKey !== self::RESIDENT_KEY_REQUIREMENT_REQUIRED) { + throw new InvalidArgumentException( + 'Invalid resident key requirement. Resident key is required but requireResidentKey is false' + ); + } + if ($this->residentKey === null && $this->requireResidentKey === true) { + $this->residentKey = self::RESIDENT_KEY_REQUIREMENT_REQUIRED; + } + $this->requireResidentKey = $requireResidentKey ?? ($residentKey === null ? null : $residentKey === self::RESIDENT_KEY_REQUIREMENT_REQUIRED); } public static function create( ?string $authenticatorAttachment = null, string $userVerification = self::USER_VERIFICATION_REQUIREMENT_PREFERRED, - null|string $residentKey = self::RESIDENT_KEY_REQUIREMENT_PREFERRED, - null|bool $requireResidentKey = false + null|string $residentKey = self::RESIDENT_KEY_REQUIREMENT_NO_PREFERENCE, + null|bool $requireResidentKey = null ): self { return new self($authenticatorAttachment, $userVerification, $residentKey, $requireResidentKey); } @@ -228,9 +233,12 @@ public function jsonSerialize(): array 'requireResidentKey' => $this->requireResidentKey, 'userVerification' => $this->userVerification, 'residentKey' => $this->residentKey, + 'authenticatorAttachment' => $this->authenticatorAttachment, ]; - if ($this->authenticatorAttachment !== null) { - $json['authenticatorAttachment'] = $this->authenticatorAttachment; + foreach ($json as $key => $value) { + if ($value === null) { + unset($json[$key]); + } } return $json; diff --git a/src/webauthn/src/CeremonyStep/CeremonyStepManagerFactory.php b/src/webauthn/src/CeremonyStep/CeremonyStepManagerFactory.php index 5c412f61b..cc58ae3bc 100644 --- a/src/webauthn/src/CeremonyStep/CeremonyStepManagerFactory.php +++ b/src/webauthn/src/CeremonyStep/CeremonyStepManagerFactory.php @@ -80,8 +80,8 @@ public function setAlgorithmManager(Manager $algorithmManager): void public function enableMetadataStatementSupport( MetadataStatementRepository $metadataStatementRepository, - StatusReportRepository $statusReportRepository, - CertificateChainValidator $certificateChainValidator + StatusReportRepository $statusReportRepository, + CertificateChainValidator $certificateChainValidator ): void { $this->metadataStatementRepository = $metadataStatementRepository; $this->statusReportRepository = $statusReportRepository; diff --git a/src/webauthn/src/CeremonyStep/CheckExtensions.php b/src/webauthn/src/CeremonyStep/CheckExtensions.php index 2ece81c74..b74aadea7 100644 --- a/src/webauthn/src/CeremonyStep/CheckExtensions.php +++ b/src/webauthn/src/CeremonyStep/CheckExtensions.php @@ -14,7 +14,7 @@ final class CheckExtensions implements CeremonyStep { public function __construct( - private readonly ExtensionOutputCheckerHandler $extensionOutputCheckerHandler, + private readonly ExtensionOutputCheckerHandler $extensionOutputCheckerHandler, ) { } diff --git a/src/webauthn/src/Denormalizer/AttestationObjectDenormalizer.php b/src/webauthn/src/Denormalizer/AttestationObjectDenormalizer.php index d3452c294..04df23a19 100644 --- a/src/webauthn/src/Denormalizer/AttestationObjectDenormalizer.php +++ b/src/webauthn/src/Denormalizer/AttestationObjectDenormalizer.php @@ -20,7 +20,7 @@ final class AttestationObjectDenormalizer implements DenormalizerInterface, Deno { use DenormalizerAwareTrait; - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { if ($this->denormalizer === null) { throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); diff --git a/src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php b/src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php index b1623f2aa..fea020bce 100644 --- a/src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php +++ b/src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php @@ -15,7 +15,7 @@ public function __construct( ) { } - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { $attestationStatementSupport = $this->attestationStatementSupportManager->get($data['fmt']); diff --git a/src/webauthn/src/Denormalizer/AuthenticationExtensionsDenormalizer.php b/src/webauthn/src/Denormalizer/AuthenticationExtensionsDenormalizer.php index 1c1677805..2ddfa8c27 100644 --- a/src/webauthn/src/Denormalizer/AuthenticationExtensionsDenormalizer.php +++ b/src/webauthn/src/Denormalizer/AuthenticationExtensionsDenormalizer.php @@ -19,7 +19,7 @@ final class AuthenticationExtensionsDenormalizer implements DenormalizerInterfac { use DenormalizerAwareTrait; - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { if ($this->denormalizer === null) { throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); diff --git a/src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php b/src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php index 23fc6cf9f..e761d6331 100644 --- a/src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php +++ b/src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php @@ -19,7 +19,7 @@ final class AuthenticatorAssertionResponseDenormalizer implements DenormalizerIn { use DenormalizerAwareTrait; - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { if ($this->denormalizer === null) { throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); diff --git a/src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php b/src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php index e76029d19..3ca44afc6 100644 --- a/src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php +++ b/src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php @@ -18,7 +18,7 @@ final class AuthenticatorAttestationResponseDenormalizer implements Denormalizer { use DenormalizerAwareTrait; - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { if ($this->denormalizer === null) { throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); diff --git a/src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php b/src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php index d057d4d6e..21b0a81f9 100644 --- a/src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php +++ b/src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php @@ -36,7 +36,7 @@ public function __construct() $this->decoder = Decoder::create(); } - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { if ($this->denormalizer === null) { throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); diff --git a/src/webauthn/src/Denormalizer/AuthenticatorResponseDenormalizer.php b/src/webauthn/src/Denormalizer/AuthenticatorResponseDenormalizer.php index 28fdfb76a..442152097 100644 --- a/src/webauthn/src/Denormalizer/AuthenticatorResponseDenormalizer.php +++ b/src/webauthn/src/Denormalizer/AuthenticatorResponseDenormalizer.php @@ -18,7 +18,7 @@ final class AuthenticatorResponseDenormalizer implements DenormalizerInterface, { use DenormalizerAwareTrait; - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { if ($this->denormalizer === null) { throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); diff --git a/src/webauthn/src/Denormalizer/CollectedClientDataDenormalizer.php b/src/webauthn/src/Denormalizer/CollectedClientDataDenormalizer.php index 5dffbbe03..faae02195 100644 --- a/src/webauthn/src/Denormalizer/CollectedClientDataDenormalizer.php +++ b/src/webauthn/src/Denormalizer/CollectedClientDataDenormalizer.php @@ -15,7 +15,7 @@ final class CollectedClientDataDenormalizer implements DenormalizerInterface, De { use DenormalizerAwareTrait; - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { if ($this->denormalizer === null) { throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); diff --git a/src/webauthn/src/Denormalizer/PublicKeyCredentialDenormalizer.php b/src/webauthn/src/Denormalizer/PublicKeyCredentialDenormalizer.php index 4da3d6b9a..e54f2bee2 100644 --- a/src/webauthn/src/Denormalizer/PublicKeyCredentialDenormalizer.php +++ b/src/webauthn/src/Denormalizer/PublicKeyCredentialDenormalizer.php @@ -19,7 +19,7 @@ final class PublicKeyCredentialDenormalizer implements DenormalizerInterface, De { use DenormalizerAwareTrait; - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { if ($this->denormalizer === null) { throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); diff --git a/src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php b/src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php index f24c229d5..1c685424c 100644 --- a/src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php +++ b/src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php @@ -24,7 +24,7 @@ final class PublicKeyCredentialOptionsDenormalizer implements DenormalizerInterf { use DenormalizerAwareTrait; - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { if ($this->denormalizer === null) { throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); diff --git a/src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php b/src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php index 8022d8432..9d327723f 100644 --- a/src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php +++ b/src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php @@ -19,7 +19,7 @@ final class PublicKeyCredentialSourceDenormalizer implements DenormalizerInterfa { use DenormalizerAwareTrait; - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { if ($this->denormalizer === null) { throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); diff --git a/src/webauthn/src/Denormalizer/PublicKeyCredentialUserEntityDenormalizer.php b/src/webauthn/src/Denormalizer/PublicKeyCredentialUserEntityDenormalizer.php index 8f376c0fc..6e4a9a1ac 100644 --- a/src/webauthn/src/Denormalizer/PublicKeyCredentialUserEntityDenormalizer.php +++ b/src/webauthn/src/Denormalizer/PublicKeyCredentialUserEntityDenormalizer.php @@ -16,7 +16,7 @@ final class PublicKeyCredentialUserEntityDenormalizer implements DenormalizerInt { use DenormalizerAwareTrait; - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { if ($this->denormalizer === null) { throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); diff --git a/src/webauthn/src/Denormalizer/TrustPathDenormalizer.php b/src/webauthn/src/Denormalizer/TrustPathDenormalizer.php index 0f53e33bc..fb020287c 100644 --- a/src/webauthn/src/Denormalizer/TrustPathDenormalizer.php +++ b/src/webauthn/src/Denormalizer/TrustPathDenormalizer.php @@ -6,30 +6,22 @@ use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Webauthn\Exception\InvalidTrustPathException; +use Webauthn\TrustPath\CertificateTrustPath; +use Webauthn\TrustPath\EcdaaKeyIdTrustPath; +use Webauthn\TrustPath\EmptyTrustPath; use Webauthn\TrustPath\TrustPath; use function array_key_exists; -use function in_array; final class TrustPathDenormalizer implements DenormalizerInterface { - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { - array_key_exists('type', $data) || throw InvalidTrustPathException::create('The trust path type is missing'); - $className = $data['type']; - if (class_exists($className) !== true) { - throw InvalidTrustPathException::create( - sprintf('The trust path type "%s" is not supported', $data['type']) - ); - } - - $implements = class_implements($className); - if (! in_array(TrustPath::class, $implements, true)) { - throw InvalidTrustPathException::create( - sprintf('The trust path type "%s" is not supported', $data['type']) - ); - } - - return $className::createFromArray($data); + return match (true) { + array_key_exists('ecdaaKeyId', $data) => new EcdaaKeyIdTrustPath($data), + array_key_exists('x5c', $data) => CertificateTrustPath::create($data), + $data === [], isset($data['type']) && $data['type'] === EmptyTrustPath::class => EmptyTrustPath::create(), + default => throw new InvalidTrustPathException('Unsupported trust path type'), + }; } public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool diff --git a/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationFailedEvent.php b/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationFailedEvent.php index 140322cab..fb542c43a 100644 --- a/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationFailedEvent.php +++ b/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationFailedEvent.php @@ -54,6 +54,9 @@ public function getCredential(): ?PublicKeyCredentialSource return $this->credentialId instanceof PublicKeyCredentialSource ? $this->credentialId : null; } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getAuthenticatorAssertionResponse(): AuthenticatorAssertionResponse { return $this->authenticatorAssertionResponse; @@ -73,11 +76,17 @@ public function getRequest(): ServerRequestInterface|string return $this->host; } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getUserHandle(): ?string { return $this->userHandle; } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getThrowable(): Throwable { return $this->throwable; diff --git a/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationSucceededEvent.php b/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationSucceededEvent.php index 347e6fa29..88442b171 100644 --- a/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationSucceededEvent.php +++ b/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationSucceededEvent.php @@ -12,12 +12,12 @@ class AuthenticatorAssertionResponseValidationSucceededEvent { public function __construct( - private readonly null|string $credentialId, - private readonly AuthenticatorAssertionResponse $authenticatorAssertionResponse, - private readonly PublicKeyCredentialRequestOptions $publicKeyCredentialRequestOptions, + public readonly null|string $credentialId, + public readonly AuthenticatorAssertionResponse $authenticatorAssertionResponse, + public readonly PublicKeyCredentialRequestOptions $publicKeyCredentialRequestOptions, public readonly ServerRequestInterface|string $host, - private readonly ?string $userHandle, - private readonly PublicKeyCredentialSource $publicKeyCredentialSource + public readonly ?string $userHandle, + public readonly PublicKeyCredentialSource $publicKeyCredentialSource ) { if ($host instanceof ServerRequestInterface) { trigger_deprecation( @@ -39,16 +39,25 @@ public function __construct( } } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getCredentialId(): string { return $this->publicKeyCredentialSource->publicKeyCredentialId; } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getAuthenticatorAssertionResponse(): AuthenticatorAssertionResponse { return $this->authenticatorAssertionResponse; } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getPublicKeyCredentialRequestOptions(): PublicKeyCredentialRequestOptions { return $this->publicKeyCredentialRequestOptions; @@ -63,11 +72,17 @@ public function getRequest(): ServerRequestInterface|string return $this->host; } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getUserHandle(): ?string { return $this->userHandle; } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getPublicKeyCredentialSource(): PublicKeyCredentialSource { return $this->publicKeyCredentialSource; diff --git a/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationFailedEvent.php b/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationFailedEvent.php index d7edd0f48..59f7403b9 100644 --- a/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationFailedEvent.php +++ b/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationFailedEvent.php @@ -12,10 +12,10 @@ class AuthenticatorAttestationResponseValidationFailedEvent { public function __construct( - private readonly AuthenticatorAttestationResponse $authenticatorAttestationResponse, - private readonly PublicKeyCredentialCreationOptions $publicKeyCredentialCreationOptions, + public readonly AuthenticatorAttestationResponse $authenticatorAttestationResponse, + public readonly PublicKeyCredentialCreationOptions $publicKeyCredentialCreationOptions, public readonly ServerRequestInterface|string $host, - private readonly Throwable $throwable + public readonly Throwable $throwable ) { if ($host instanceof ServerRequestInterface) { trigger_deprecation( @@ -30,11 +30,17 @@ public function __construct( } } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getAuthenticatorAttestationResponse(): AuthenticatorAttestationResponse { return $this->authenticatorAttestationResponse; } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getPublicKeyCredentialCreationOptions(): PublicKeyCredentialCreationOptions { return $this->publicKeyCredentialCreationOptions; @@ -49,6 +55,9 @@ public function getRequest(): ServerRequestInterface|string return $this->host; } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getThrowable(): Throwable { return $this->throwable; diff --git a/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationSucceededEvent.php b/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationSucceededEvent.php index aae7af83d..59cca9516 100644 --- a/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationSucceededEvent.php +++ b/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationSucceededEvent.php @@ -12,10 +12,10 @@ class AuthenticatorAttestationResponseValidationSucceededEvent { public function __construct( - private readonly AuthenticatorAttestationResponse $authenticatorAttestationResponse, - private readonly PublicKeyCredentialCreationOptions $publicKeyCredentialCreationOptions, + public readonly AuthenticatorAttestationResponse $authenticatorAttestationResponse, + public readonly PublicKeyCredentialCreationOptions $publicKeyCredentialCreationOptions, public readonly ServerRequestInterface|string $host, - private readonly PublicKeyCredentialSource $publicKeyCredentialSource + public readonly PublicKeyCredentialSource $publicKeyCredentialSource ) { if ($host instanceof ServerRequestInterface) { trigger_deprecation( @@ -30,11 +30,17 @@ public function __construct( } } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getAuthenticatorAttestationResponse(): AuthenticatorAttestationResponse { return $this->authenticatorAttestationResponse; } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getPublicKeyCredentialCreationOptions(): PublicKeyCredentialCreationOptions { return $this->publicKeyCredentialCreationOptions; @@ -49,6 +55,9 @@ public function getRequest(): ServerRequestInterface|string return $this->host; } + /** + * @deprecated since 4.8.0. Will be removed in 5.0.0. Please use the property instead. + */ public function getPublicKeyCredentialSource(): PublicKeyCredentialSource { return $this->publicKeyCredentialSource; diff --git a/tests/library/Functional/AndroidSafetyNetAttestationStatementTest.php b/tests/library/Functional/AndroidSafetyNetAttestationStatementTest.php index 1f45319e7..1a2546b97 100644 --- a/tests/library/Functional/AndroidSafetyNetAttestationStatementTest.php +++ b/tests/library/Functional/AndroidSafetyNetAttestationStatementTest.php @@ -36,7 +36,7 @@ public function anAndroidSafetyNetAttestationCanBeVerified(): void 'kmns43CWVswbMovrKPkgd1lEpc6LZdfk0UQ/nuZbp00jW5C61PEW1dNaptZ0GkrIK9WRtaAXWkndIEEBgNICRw', true ), - [PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_ES256)], + [PublicKeyCredentialParameters::createPk(Algorithms::COSE_ALGORITHM_ES256)], attestation: PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT, ); $publicKeyCredential = $this->getSerializer() diff --git a/tests/library/Functional/AppleAttestationStatementTest.php b/tests/library/Functional/AppleAttestationStatementTest.php index f0a8b0088..97bbba97a 100644 --- a/tests/library/Functional/AppleAttestationStatementTest.php +++ b/tests/library/Functional/AppleAttestationStatementTest.php @@ -68,11 +68,11 @@ public function anAppleAttestationCanBeVerified(): void /** @var AuthenticatorData $authenticatorData */ $authenticatorData = $publicKeyCredential->response ->attestationObject - ->getAuthData(); + ->authData; /** @var AttestationStatement $attestationStatement */ $attestationStatement = $publicKeyCredential->response ->attestationObject - ->getAttStmt(); + ->attStmt; static::assertSame(AttestationStatement::TYPE_ANONCA, $attestationStatement->type); static::assertSame( hex2bin('3ddc4710e9c088b229dba89d563220bb39f7229aff465b0a656b1afb9a8af8a0'), diff --git a/tests/library/Functional/AttestationStatementWithTokenBindingTest.php b/tests/library/Functional/AttestationStatementWithTokenBindingTest.php index 1c752f38b..c99a3f9f3 100644 --- a/tests/library/Functional/AttestationStatementWithTokenBindingTest.php +++ b/tests/library/Functional/AttestationStatementWithTokenBindingTest.php @@ -37,7 +37,7 @@ public function anAttestationWithTokenBindingCanBeVerified(): void 'SkehMoAkGv+cqmuiEqpOgGhswj5oDa9kIPxgG1IihzkxPe4LNfP8bUyFiNn/MXBlqiOY6IpHFZl1XfIM07kRZw==', true ), - [PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_ES256)], + [PublicKeyCredentialParameters::createPk(Algorithms::COSE_ALGORITHM_ES256)], attestation: PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT, ); $publicKeyCredential = $this->getSerializer() diff --git a/tests/library/Functional/Fido2AttestationStatementTest.php b/tests/library/Functional/Fido2AttestationStatementTest.php index f09c81216..d6d93d422 100644 --- a/tests/library/Functional/Fido2AttestationStatementTest.php +++ b/tests/library/Functional/Fido2AttestationStatementTest.php @@ -33,7 +33,7 @@ public function aFidoU2FAttestationCanBeVerified(): void 'pGRaBff9zpaw3CDAsggpOMRonJaqMXYjkvIGTPt3rHH+53RCW7LQ9l4NmGcv8dNZSNLDrvQDKaSNhFjviggcZA==', true ), - [PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_ES256)], + [PublicKeyCredentialParameters::createPk(Algorithms::COSE_ALGORITHM_ES256)], attestation: PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT ); $publicKeyCredential = $this->getSerializer() diff --git a/tests/library/Unit/AuthenticatorSelectionCriteriaTest.php b/tests/library/Unit/AuthenticatorSelectionCriteriaTest.php index e1780b8eb..f03aae892 100644 --- a/tests/library/Unit/AuthenticatorSelectionCriteriaTest.php +++ b/tests/library/Unit/AuthenticatorSelectionCriteriaTest.php @@ -18,12 +18,11 @@ final class AuthenticatorSelectionCriteriaTest extends AbstractTestCase public function anAuthenticatorSelectionCriteriaCanBeCreatedAndValueAccessed(): void { // Given - $expectedJson = '{"requireResidentKey":false,"userVerification":"required","residentKey":"preferred","authenticatorAttachment":"platform"}'; + $expectedJson = '{"userVerification":"required","authenticatorAttachment":"platform"}'; $authenticatorSelectionCriteria = AuthenticatorSelectionCriteria::create( AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_PLATFORM, AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED, - AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_NO_PREFERENCE, - false + AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_NO_PREFERENCE ); //When @@ -39,8 +38,8 @@ public function anAuthenticatorSelectionCriteriaCanBeCreatedAndValueAccessed(): AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_PLATFORM, $data->authenticatorAttachment ); - static::assertFalse($data->requireResidentKey); - static::assertSame(AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_PREFERRED, $data->residentKey); + static::assertNull($data->requireResidentKey); + static::assertSame(AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_NO_PREFERENCE, $data->residentKey); static::assertSame($expectedJson, json_encode($data, JSON_THROW_ON_ERROR)); static::assertSame($expectedJson, json_encode($authenticatorSelectionCriteria, JSON_THROW_ON_ERROR)); } diff --git a/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php b/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php index 488a041a8..d2ed3912e 100644 --- a/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php +++ b/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php @@ -15,7 +15,6 @@ use Symfony\Component\Serializer\SerializerInterface; use Webauthn\Bundle\Security\Authentication\Token\WebauthnToken; use Webauthn\Bundle\Security\Storage\Item; -use Webauthn\Bundle\Security\Storage\OptionsStorage; use Webauthn\PublicKeyCredential; use Webauthn\PublicKeyCredentialCreationOptions; use Webauthn\PublicKeyCredentialDescriptor; @@ -35,28 +34,18 @@ */ final class AdditionalAuthenticatorTest extends WebTestCase { - private KernelBrowser $client; - - private OptionsStorage $storage; - - protected function setUp(): void - { - $this->client = static::createClient([], [ - 'HTTPS' => 'on', - ]); - - $this->storage = static::getContainer()->get(CustomSessionStorage::class); - } - #[Test] public function anExistingUserCanAskForOptionsUsingTheDedicatedController(): void { - $this->logIn(); - $this->client->request(Request::METHOD_POST, '/devices/add/options', [], [], [ + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); + $this->logIn($client); + $client->request(Request::METHOD_POST, '/devices/add/options', [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_HOST' => 'test.com', ], json_encode([], JSON_THROW_ON_ERROR)); - $response = $this->client->getResponse(); + $response = $client->getResponse(); $data = json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR); static::assertResponseIsSuccessful(); @@ -92,11 +81,13 @@ public function thePublicKeyCredentialDataCanBeLoaded(): void #[Depends('thePublicKeyCredentialDataCanBeLoaded')] public function withTheOptionAnExistingUserCanRegisterNewAnotherAuthenticator(): void { + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); /** @var PublicKeyCredentialSourceRepository $publicKeyCredentialSourceRepository */ - $publicKeyCredentialSourceRepository = self::$kernel - ->getContainer() + $publicKeyCredentialSourceRepository = $client->getContainer() ->get(PublicKeyCredentialSourceRepository::class); - $this->logIn(); + $this->logIn($client); $publicKeyCredentialUserEntity = PublicKeyCredentialUserEntity::create('test@foo.com', random_bytes( 64 @@ -109,10 +100,11 @@ public function withTheOptionAnExistingUserCanRegisterNewAnotherAuthenticator(): '9WqgpRIYvGMCUYiFT20o1U7hSD193k11zu4tKP7wRcrE26zs1zc4LHyPinvPGS86wu6bDvpwbt8Xp2bQ3VBRSQ==', true ), - [PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_ES256)] + [PublicKeyCredentialParameters::createPk(Algorithms::COSE_ALGORITHM_ES256)] ); - $this->storage->store(Item::create( + $storage = static::getContainer()->get(CustomSessionStorage::class); + $storage->store(Item::create( $publicKeyCredentialCreationOptions, $publicKeyCredentialCreationOptions->user )); @@ -124,7 +116,7 @@ public function withTheOptionAnExistingUserCanRegisterNewAnotherAuthenticator(): $publicKeyCredentialSourceRepository->findAllForUserEntity($publicKeyCredentialCreationOptions->user) ); $body = '{"id":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB_MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1-RIuTF9DUtEJZEEK","type":"public-key","rawId":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB/MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1+RIuTF9DUtEJZEEK","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOiI5V3FncFJJWXZHTUNVWWlGVDIwbzFVN2hTRDE5M2sxMXp1NHRLUDd3UmNyRTI2enMxemM0TEh5UGludlBHUzg2d3U2YkR2cHdidDhYcDJiUTNWQlJTUSIsImNsaWVudEV4dGVuc2lvbnMiOnt9LCJoYXNoQWxnb3JpdGhtIjoiU0hBLTI1NiIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0Ojg0NDMiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0","attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjkSZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2NBAAAAAAAAAAAAAAAAAAAAAAAAAAAAYJjIobiMfS7pLMMQTjIzBw3+hADjTsu6nVoWkEO3TrVYkdnFQfzDW2cVEYtnL4ErykiC295iEnvZTzRvbGIKI7mOYjYp2DoOoUVcZptFbLLjRtqZtfkSLkxfQ1LRCWRBCqUBAgMmIAEhWCAcPxwKyHADVjTgTsat4R/Jax6PWte50A8ZasMm4w6RxCJYILt0FCiGwC6rBrh3ySNy0yiUjZpNGAhW+aM9YYyYnUTJ"}}'; - $this->client->request( + $client->request( Request::METHOD_POST, '/devices/add', [], @@ -135,7 +127,7 @@ public function withTheOptionAnExistingUserCanRegisterNewAnotherAuthenticator(): ], $body ); - $response = $this->client->getResponse(); + $response = $client->getResponse(); $data = json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR); static::assertResponseStatusCodeSame(201); @@ -155,12 +147,15 @@ public function withTheOptionAnExistingUserCanRegisterNewAnotherAuthenticator(): #[Test] public function anExistingUserCanGetOptionsTestItsAuthenticators(): void { - $this->logIn(); - $this->client->request(Request::METHOD_POST, '/devices/test/options', [], [], [ + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); + $this->logIn($client); + $client->request(Request::METHOD_POST, '/devices/test/options', [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_HOST' => 'test.com', ], json_encode([], JSON_THROW_ON_ERROR)); - $response = $this->client->getResponse(); + $response = $client->getResponse(); $data = json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR); static::assertResponseIsSuccessful(); @@ -172,7 +167,7 @@ public function anExistingUserCanGetOptionsTestItsAuthenticators(): void static::assertSame('ok', $data['status']); } - private function logIn(): void + private function logIn(KernelBrowser $client): void { /** @var SerializerInterface $serializer */ $serializer = static::getContainer()->get(SerializerInterface::class); @@ -231,13 +226,13 @@ private function logIn(): void $domains = array_unique( array_map( static fn (Cookie $cookie) => $cookie->getName() === $session->getName() ? $cookie->getDomain() : '', - $this->client->getCookieJar() + $client->getCookieJar() ->all() ) ) ?: ['']; foreach ($domains as $domain) { $cookie = new Cookie($session->getName(), $session->getId(), null, null, $domain); - $this->client->getCookieJar() + $client->getCookieJar() ->set($cookie); } } diff --git a/tests/symfony/functional/Firewall/RegistrationAreaTest.php b/tests/symfony/functional/Firewall/RegistrationAreaTest.php index da79de6bf..473830a2a 100644 --- a/tests/symfony/functional/Firewall/RegistrationAreaTest.php +++ b/tests/symfony/functional/Firewall/RegistrationAreaTest.php @@ -6,11 +6,9 @@ use Cose\Algorithms; use PHPUnit\Framework\Attributes\Test; -use Symfony\Bundle\FrameworkBundle\KernelBrowser; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\HttpFoundation\Request; use Webauthn\Bundle\Security\Storage\Item; -use Webauthn\Bundle\Security\Storage\OptionsStorage; use Webauthn\PublicKeyCredentialCreationOptions; use Webauthn\PublicKeyCredentialParameters; use Webauthn\PublicKeyCredentialRpEntity; @@ -29,30 +27,20 @@ */ final class RegistrationAreaTest extends WebTestCase { - private KernelBrowser $client; - - private OptionsStorage $storage; - - protected function setUp(): void - { - $this->client = static::createClient([], [ - 'HTTPS' => 'on', - ]); - - $this->storage = static::getContainer()->get(CustomSessionStorage::class); - } - #[Test] public function aRequestWithoutUsernameCanBeProcessed(): void { $content = [ 'displayName' => 'FOO', ]; - $this->client->request(Request::METHOD_POST, '/api/register/options', [], [], [ + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); + $client->request(Request::METHOD_POST, '/api/register/options', [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_HOST' => 'test.com', ], json_encode($content)); - $response = $this->client->getResponse(); + $response = $client->getResponse(); $data = json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR); static::assertArrayHasKey('status', $data); @@ -66,11 +54,14 @@ public function aRequestWithoutDisplayNameCanBeProcessed(): void $content = [ 'username' => 'foo', ]; - $this->client->request(Request::METHOD_POST, '/api/register/options', [], [], [ + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); + $client->request(Request::METHOD_POST, '/api/register/options', [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_HOST' => 'test.com', ], json_encode($content)); - $response = $this->client->getResponse(); + $response = $client->getResponse(); $data = json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR); static::assertArrayHasKey('status', $data); @@ -86,16 +77,19 @@ public function aValidRequestProcessed(): void 'displayName' => 'FOO', 'authenticatorSelection' => [ 'authenticatorAttachment' => 'cross-platform', - 'userVerification' => 'preferred', + 'userVerification' => 'required', 'requireResidentKey' => true, ], 'attestation' => 'indirect', ]; - $this->client->request(Request::METHOD_POST, '/api/register/options', [], [], [ + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); + $client->request(Request::METHOD_POST, '/api/register/options', [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_HOST' => 'test.com', ], json_encode($content)); - $response = $this->client->getResponse(); + $response = $client->getResponse(); $data = json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR); static::assertArrayHasKey('status', $data); @@ -110,7 +104,7 @@ public function aValidRequestProcessed(): void static::assertArrayHasKey('authenticatorSelection', $data); static::assertSame([ 'requireResidentKey' => true, - 'userVerification' => 'preferred', + 'userVerification' => 'required', 'residentKey' => 'required', 'authenticatorAttachment' => 'cross-platform', ], $data['authenticatorSelection']); @@ -126,11 +120,14 @@ public function aValidRequestProcessedOnOtherHost(): void 'requireResidentKey' => true, ], ]; - $this->client->request(Request::METHOD_POST, '/api/register/options', [], [], [ + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); + $client->request(Request::METHOD_POST, '/api/register/options', [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_HOST' => 'foo.com', ], json_encode($content)); - $response = $this->client->getResponse(); + $response = $client->getResponse(); $data = json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR); static::assertArrayHasKey('status', $data); @@ -166,11 +163,14 @@ public function aValidRequestProcessedWithExtensions(): void 'def' => '123', ], ]; - $this->client->request(Request::METHOD_POST, '/api/register/options', [], [], [ + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); + $client->request(Request::METHOD_POST, '/api/register/options', [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_HOST' => 'test.com', ], json_encode($content)); - $response = $this->client->getResponse(); + $response = $client->getResponse(); $data = json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR); static::assertArrayHasKey('status', $data); @@ -195,9 +195,6 @@ public function aValidRequestProcessedWithExtensions(): void ], $data['authenticatorSelection']); } - /** - * Note that this use case should fail on the attestation response step - */ #[Test] public function aRegistrationOptionsRequestCanBeAcceptedForExistingUsers(): void { @@ -205,11 +202,14 @@ public function aRegistrationOptionsRequestCanBeAcceptedForExistingUsers(): void 'username' => 'admin', 'displayName' => 'Admin', ]; - $this->client->request(Request::METHOD_POST, '/api/register/options', [], [], [ + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); + $client->request(Request::METHOD_POST, '/api/register/options', [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_HOST' => 'test.com', ], json_encode($content)); - $response = $this->client->getResponse(); + $response = $client->getResponse(); $data = json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR); static::assertArrayHasKey('status', $data); @@ -222,11 +222,14 @@ public function aRegistrationResultRequestCannotBeAcceptedIfNoOptionsAreAvailabl { $content = '{"id":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB_MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1-RIuTF9DUtEJZEEK","type":"public-key","rawId":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB/MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1+RIuTF9DUtEJZEEK","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOiI5V3FncFJJWXZHTUNVWWlGVDIwbzFVN2hTRDE5M2sxMXp1NHRLUDd3UmNyRTI2enMxemM0TEh5UGludlBHUzg2d3U2YkR2cHdidDhYcDJiUTNWQlJTUSIsImNsaWVudEV4dGVuc2lvbnMiOnt9LCJoYXNoQWxnb3JpdGhtIjoiU0hBLTI1NiIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0Ojg0NDMiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0","attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjkSZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2NBAAAAAAAAAAAAAAAAAAAAAAAAAAAAYJjIobiMfS7pLMMQTjIzBw3+hADjTsu6nVoWkEO3TrVYkdnFQfzDW2cVEYtnL4ErykiC295iEnvZTzRvbGIKI7mOYjYp2DoOoUVcZptFbLLjRtqZtfkSLkxfQ1LRCWRBCqUBAgMmIAEhWCAcPxwKyHADVjTgTsat4R/Jax6PWte50A8ZasMm4w6RxCJYILt0FCiGwC6rBrh3ySNy0yiUjZpNGAhW+aM9YYyYnUTJ"}}'; - $this->client->request(Request::METHOD_POST, '/api/register', [], [], [ + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); + $client->request(Request::METHOD_POST, '/api/register', [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_HOST' => 'test.com', ], $content); - $response = $this->client->getResponse(); + $response = $client->getResponse(); $data = json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR); static::assertArrayHasKey('status', $data); @@ -250,22 +253,26 @@ public function aValidRegistrationResultRequestIsCorrectlyManaged(): void '9WqgpRIYvGMCUYiFT20o1U7hSD193k11zu4tKP7wRcrE26zs1zc4LHyPinvPGS86wu6bDvpwbt8Xp2bQ3VBRSQ==', true ), - [PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_ES256)] + [PublicKeyCredentialParameters::createPk(Algorithms::COSE_ALGORITHM_ES256)] ); $content = '{"id":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB_MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1-RIuTF9DUtEJZEEK","type":"public-key","rawId":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB/MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1+RIuTF9DUtEJZEEK","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOiI5V3FncFJJWXZHTUNVWWlGVDIwbzFVN2hTRDE5M2sxMXp1NHRLUDd3UmNyRTI2enMxemM0TEh5UGludlBHUzg2d3U2YkR2cHdidDhYcDJiUTNWQlJTUSIsImNsaWVudEV4dGVuc2lvbnMiOnt9LCJoYXNoQWxnb3JpdGhtIjoiU0hBLTI1NiIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0Ojg0NDMiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0","attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjkSZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2NBAAAAAAAAAAAAAAAAAAAAAAAAAAAAYJjIobiMfS7pLMMQTjIzBw3+hADjTsu6nVoWkEO3TrVYkdnFQfzDW2cVEYtnL4ErykiC295iEnvZTzRvbGIKI7mOYjYp2DoOoUVcZptFbLLjRtqZtfkSLkxfQ1LRCWRBCqUBAgMmIAEhWCAcPxwKyHADVjTgTsat4R/Jax6PWte50A8ZasMm4w6RxCJYILt0FCiGwC6rBrh3ySNy0yiUjZpNGAhW+aM9YYyYnUTJ"}}'; - $pkcsRepository = $this->client->getContainer() + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); + $pkcsRepository = $client->getContainer() ->get(PublicKeyCredentialSourceRepository::class); $pkcsRepository->clearCredentials(); - $this->storage->store(Item::create($publicKeyCredentialCreationOptions, $publicKeyCredentialUserEntity)); + $storage = static::getContainer()->get(CustomSessionStorage::class); + $storage->store(Item::create($publicKeyCredentialCreationOptions, $publicKeyCredentialUserEntity)); - $this->client->request(Request::METHOD_POST, '/api/register', [], [], [ + $client->request(Request::METHOD_POST, '/api/register', [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_HOST' => 'localhost', ], $content); - $response = $this->client->getResponse(); + $response = $client->getResponse(); $data = json_decode($response->getContent(), true, 512, JSON_THROW_ON_ERROR); static::assertArrayHasKey('status', $data); @@ -274,12 +281,12 @@ public function aValidRegistrationResultRequestIsCorrectlyManaged(): void static::assertArrayHasKey('errorMessage', $data); static::assertSame('', $data['errorMessage']); - $pkueRepository = $this->client->getContainer() + $pkueRepository = $client->getContainer() ->get(PublicKeyCredentialUserEntityRepository::class); $user = $pkueRepository->findOneByUsername('test@foo.com'); static::assertInstanceOf(User::class, $user); - static::assertTrue($this->client->getRequest()->getSession()->has('_security_main')); - static::assertTrue($this->client->getResponse()->headers->has('set-cookie')); + static::assertTrue($client->getRequest()->getSession()->has('_security_main')); + static::assertTrue($client->getResponse()->headers->has('set-cookie')); } } diff --git a/tests/symfony/functional/Firewall/SecuredAreaTest.php b/tests/symfony/functional/Firewall/SecuredAreaTest.php index 7ccef63ba..a610edf3f 100644 --- a/tests/symfony/functional/Firewall/SecuredAreaTest.php +++ b/tests/symfony/functional/Firewall/SecuredAreaTest.php @@ -6,11 +6,9 @@ use ParagonIE\ConstantTime\Base64UrlSafe; use PHPUnit\Framework\Attributes\Test; -use Symfony\Bundle\FrameworkBundle\KernelBrowser; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\HttpFoundation\Response; use Webauthn\Bundle\Security\Storage\Item; -use Webauthn\Bundle\Security\Storage\OptionsStorage; use Webauthn\PublicKeyCredentialDescriptor; use Webauthn\PublicKeyCredentialRequestOptions; use Webauthn\PublicKeyCredentialUserEntity; @@ -22,23 +20,13 @@ */ final class SecuredAreaTest extends WebTestCase { - private KernelBrowser $client; - - private OptionsStorage $storage; - - protected function setUp(): void - { - $this->client = static::createClient([], [ - 'HTTPS' => 'on', - ]); - - $this->storage = static::getContainer()->get(CustomSessionStorage::class); - } - #[Test] public function aClientCannotAccessToTheResourceIfUserIsNotAuthenticated(): void { - $this->client->request('GET', '/admin', [], [], [ + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); + $client->request('GET', '/admin', [], [], [ 'HTTPS' => 'on', ]); @@ -51,14 +39,17 @@ public function aClientCanSubmitUsernameToGetWebauthnOptions(): void $body = [ 'username' => 'admin', ]; - $this->client->request('POST', '/api/login/options', [], [], [ + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); + $client->request('POST', '/api/login/options', [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_HOST' => 'test.com', 'HTTPS' => 'on', ], json_encode($body, JSON_THROW_ON_ERROR)); static::assertResponseIsSuccessful(); - $json = json_decode($this->client->getResponse()->getContent(), true, 512, JSON_THROW_ON_ERROR); + $json = json_decode($client->getResponse()->getContent(), true, 512, JSON_THROW_ON_ERROR); static::assertArrayHasKey('challenge', $json); static::assertArrayHasKey('rpId', $json); static::assertArrayHasKey('userVerification', $json); @@ -71,7 +62,10 @@ public function aUserCannotBeBeAuthenticatedInAbsenceOfOptions(): void { $assertion = '{"id":"eHouz_Zi7-BmByHjJ_tx9h4a1WZsK4IzUmgGjkhyOodPGAyUqUp_B9yUkflXY3yHWsNtsrgCXQ3HjAIFUeZB-w","type":"public-key","rawId":"eHouz/Zi7+BmByHjJ/tx9h4a1WZsK4IzUmgGjkhyOodPGAyUqUp/B9yUkflXY3yHWsNtsrgCXQ3HjAIFUeZB+w==","response":{"authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAAew","clientDataJSON":"eyJjaGFsbGVuZ2UiOiJHMEpiTExuZGVmM2EwSXkzUzJzU1FBOHVPNFNPX3plNkZaTUF1UEk2LXhJIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6ODQ0MyIsInR5cGUiOiJ3ZWJhdXRobi5nZXQifQ","signature":"MEUCIEY/vcNkbo/LdMTfLa24ZYLlMMVMRd8zXguHBvqud9AJAiEAwCwpZpvcMaqCrwv85w/8RGiZzE+gOM61ffxmgEDeyhM=","userHandle":null}}'; - $this->client->request('POST', '/api/login', [], [], [ + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); + $client->request('POST', '/api/login', [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_HOST' => 'test.com', ], $assertion); @@ -79,7 +73,7 @@ public function aUserCannotBeBeAuthenticatedInAbsenceOfOptions(): void self::assertResponseStatusCodeSame(Response::HTTP_UNAUTHORIZED); static::assertSame( '{"status":"error","errorMessage":"No public key credential options available for this session."}', - $this->client->getResponse() + $client->getResponse() ->getContent() ); } @@ -87,6 +81,9 @@ public function aUserCannotBeBeAuthenticatedInAbsenceOfOptions(): void #[Test] public function aUserCanBeAuthenticatedAndAccessToTheProtectedResource(): void { + $client = static::createClient([], [ + 'HTTPS' => 'on', + ]); $publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::create( base64_decode('G0JbLLndef3a0Iy3S2sSQA8uO4SO/ze6FZMAuPI6+xI=', true) ); @@ -102,14 +99,15 @@ public function aUserCanBeAuthenticatedAndAccessToTheProtectedResource(): void ), ]; - $this->storage->store(Item::create( + $storage = static::getContainer()->get(CustomSessionStorage::class); + $storage->store(Item::create( $publicKeyCredentialRequestOptions, PublicKeyCredentialUserEntity::create('admin', 'foo', 'Foo BAR (-_-)') )); $assertion = '{"id":"eHouz_Zi7-BmByHjJ_tx9h4a1WZsK4IzUmgGjkhyOodPGAyUqUp_B9yUkflXY3yHWsNtsrgCXQ3HjAIFUeZB-w","type":"public-key","rawId":"eHouz/Zi7+BmByHjJ/tx9h4a1WZsK4IzUmgGjkhyOodPGAyUqUp/B9yUkflXY3yHWsNtsrgCXQ3HjAIFUeZB+w==","response":{"authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAAew","clientDataJSON":"eyJjaGFsbGVuZ2UiOiJHMEpiTExuZGVmM2EwSXkzUzJzU1FBOHVPNFNPX3plNkZaTUF1UEk2LXhJIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6ODQ0MyIsInR5cGUiOiJ3ZWJhdXRobi5nZXQifQ","signature":"MEUCIEY/vcNkbo/LdMTfLa24ZYLlMMVMRd8zXguHBvqud9AJAiEAwCwpZpvcMaqCrwv85w/8RGiZzE+gOM61ffxmgEDeyhM=","userHandle":null}}'; - $this->client->request('POST', '/api/login', [], [], [ + $client->request('POST', '/api/login', [], [], [ 'CONTENT_TYPE' => 'application/json', 'HTTP_HOST' => 'localhost', ], $assertion); @@ -117,14 +115,14 @@ public function aUserCanBeAuthenticatedAndAccessToTheProtectedResource(): void static::assertResponseIsSuccessful(); static::assertSame( '{"status":"ok","errorMessage":"","userIdentifier":"admin"}', - $this->client->getResponse() + $client->getResponse() ->getContent() ); - static::assertTrue($this->client->getRequest()->getSession()->has('_security_main')); + static::assertTrue($client->getRequest()->getSession()->has('_security_main')); - $this->client->request('GET', '/admin'); + $client->request('GET', '/admin'); - static::assertSame('["Hello admin"]', $this->client->getResponse()->getContent()); + static::assertSame('["Hello admin"]', $client->getResponse()->getContent()); static::assertResponseIsSuccessful(); } }