From 3da7335d420069432defe77e0c1308fedfc3d7a5 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sun, 30 Jun 2024 17:06:19 +0200 Subject: [PATCH 1/7] Remove debug dump in ProfileBasedRequestOptionsBuilder The update removes a debug dump in the ProfileBasedRequestOptionsBuilder. This dump was outputting the results of the fake credential generator if it's defined in cases when a --- .../ProfileBasedRequestOptionsBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/symfony/src/CredentialOptionsBuilder/ProfileBasedRequestOptionsBuilder.php b/src/symfony/src/CredentialOptionsBuilder/ProfileBasedRequestOptionsBuilder.php index 839ae4961..e0658324e 100644 --- a/src/symfony/src/CredentialOptionsBuilder/ProfileBasedRequestOptionsBuilder.php +++ b/src/symfony/src/CredentialOptionsBuilder/ProfileBasedRequestOptionsBuilder.php @@ -73,7 +73,7 @@ public function getFromRequest( $userEntity = $optionsRequest->username === null ? null : $this->userEntityRepository->findOneByUsername( $optionsRequest->username ); - dump($this->fakeCredentialGenerator?->generate($request, $optionsRequest->username ?? '')); + $allowedCredentials = match (true) { $userEntity === null && $optionsRequest->username === null, $userEntity === null && $optionsRequest->username !== null && $this->fakeCredentialGenerator === null => [], $userEntity === null && $optionsRequest->username !== null && $this->fakeCredentialGenerator !== null => $this->fakeCredentialGenerator->generate( From 64de11f6cddc71e56c76e0cc4573bf94d02be045 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sat, 6 Jul 2024 06:53:56 +0200 Subject: [PATCH 2/7] Add feature to hide existing credentials The code changes enable suppressing the existing user credential details to enhance security. This feature introduces a preventive measure against username enumeration exploits by concealing the previously existing credentials. The flag 'hide_existing_credentials' has been added to facilitate this change, defaulted to false. This change is particularly important during the attestation ceremony performed by anonymous users. --- phpstan-baseline.neon | 15 ++++++++++----- .../AttestationControllerFactory.php | 4 +++- .../AttestationRequestController.php | 7 ++++++- .../ProfileBasedCreationOptionsBuilder.php | 5 +++-- ...blicKeyCredentialCreationOptionsBuilder.php | 3 ++- .../src/DependencyInjection/Configuration.php | 6 ++++++ .../Factory/Security/WebauthnFactory.php | 1 + .../DependencyInjection/WebauthnExtension.php | 1 + tests/symfony/config/config.yml | 1 + .../AdditionalAuthenticatorTest.php | 1 + .../PublicKeyCredentialSourceRepository.php | 18 ++++++++++++++++++ 11 files changed, 52 insertions(+), 10 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ac589c129..bc26eb029 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -692,6 +692,11 @@ parameters: count: 1 path: src/symfony/src/Controller/AttestationControllerFactory.php + - + message: "#^Method Webauthn\\\\Bundle\\\\CredentialOptionsBuilder\\\\PublicKeyCredentialCreationOptionsBuilder\\:\\:getFromRequest\\(\\) invoked with 3 parameters, 2 required\\.$#" + count: 1 + path: src/symfony/src/Controller/AttestationRequestController.php + - message: "#^Call to an undefined method Symfony\\\\Component\\\\HttpFoundation\\\\Request\\:\\:getContentType\\(\\)\\.$#" count: 1 @@ -807,11 +812,6 @@ parameters: count: 1 path: src/symfony/src/CredentialOptionsBuilder/ProfileBasedRequestOptionsBuilder.php - - - message: "#^Should not use function \"dump\", please change the code\\.$#" - count: 1 - path: src/symfony/src/CredentialOptionsBuilder/ProfileBasedRequestOptionsBuilder.php - - message: """ #^Fetching class constant class of deprecated class Webauthn\\\\Bundle\\\\Event\\\\AuthenticatorAssertionResponseValidationFailedEvent\\: @@ -1061,6 +1061,11 @@ parameters: count: 4 path: src/symfony/src/DependencyInjection/WebauthnExtension.php + - + message: "#^Cannot access offset 'hide_existing…' on mixed\\.$#" + count: 1 + path: src/symfony/src/DependencyInjection/WebauthnExtension.php + - message: "#^Cannot access offset 'host' on mixed\\.$#" count: 4 diff --git a/src/symfony/src/Controller/AttestationControllerFactory.php b/src/symfony/src/Controller/AttestationControllerFactory.php index 286a067e0..d2fef6a0a 100644 --- a/src/symfony/src/Controller/AttestationControllerFactory.php +++ b/src/symfony/src/Controller/AttestationControllerFactory.php @@ -72,13 +72,15 @@ public function createRequestController( OptionsStorage $optionStorage, CreationOptionsHandler $creationOptionsHandler, FailureHandler|AuthenticationFailureHandlerInterface $failureHandler, + bool $hideExistingExcludedCredentials = false ): AttestationRequestController { return new AttestationRequestController( $optionsBuilder, $userEntityGuesser, $optionStorage, $creationOptionsHandler, - $failureHandler + $failureHandler, + $hideExistingExcludedCredentials ); } diff --git a/src/symfony/src/Controller/AttestationRequestController.php b/src/symfony/src/Controller/AttestationRequestController.php index 314fe14d6..70f86b512 100644 --- a/src/symfony/src/Controller/AttestationRequestController.php +++ b/src/symfony/src/Controller/AttestationRequestController.php @@ -24,6 +24,7 @@ public function __construct( private readonly OptionsStorage $optionsStorage, private readonly CreationOptionsHandler $creationOptionsHandler, private readonly FailureHandler|AuthenticationFailureHandlerInterface $failureHandler, + private readonly bool $hideExistingExcludedCredentials = false, ) { } @@ -31,7 +32,11 @@ public function __invoke(Request $request): Response { try { $userEntity = $this->userEntityGuesser->findUserEntity($request); - $publicKeyCredentialCreationOptions = $this->extractor->getFromRequest($request, $userEntity); + $publicKeyCredentialCreationOptions = $this->extractor->getFromRequest( + $request, + $userEntity, + $this->hideExistingExcludedCredentials + ); $response = $this->creationOptionsHandler->onCreationOptions( $publicKeyCredentialCreationOptions, diff --git a/src/symfony/src/CredentialOptionsBuilder/ProfileBasedCreationOptionsBuilder.php b/src/symfony/src/CredentialOptionsBuilder/ProfileBasedCreationOptionsBuilder.php index 9d1dd498e..7b9a10f8f 100644 --- a/src/symfony/src/CredentialOptionsBuilder/ProfileBasedCreationOptionsBuilder.php +++ b/src/symfony/src/CredentialOptionsBuilder/ProfileBasedCreationOptionsBuilder.php @@ -48,7 +48,8 @@ public function __construct( public function getFromRequest( Request $request, - PublicKeyCredentialUserEntity $userEntity + PublicKeyCredentialUserEntity $userEntity, + bool $hideExistingExcludedCredentials = false ): PublicKeyCredentialCreationOptions { $format = method_exists( $request, @@ -57,7 +58,7 @@ public function getFromRequest( $format === 'json' || throw new BadRequestHttpException('Only JSON content type allowed'); $content = $request->getContent(); - $excludedCredentials = $this->getCredentials($userEntity); + $excludedCredentials = $hideExistingExcludedCredentials === true ? [] : $this->getCredentials($userEntity); $optionsRequest = $this->getServerPublicKeyCredentialCreationOptionsRequest($content); $authenticatorSelectionData = $optionsRequest->authenticatorSelection; $authenticatorSelection = null; diff --git a/src/symfony/src/CredentialOptionsBuilder/PublicKeyCredentialCreationOptionsBuilder.php b/src/symfony/src/CredentialOptionsBuilder/PublicKeyCredentialCreationOptionsBuilder.php index 634d3ce8f..12e3c92c6 100644 --- a/src/symfony/src/CredentialOptionsBuilder/PublicKeyCredentialCreationOptionsBuilder.php +++ b/src/symfony/src/CredentialOptionsBuilder/PublicKeyCredentialCreationOptionsBuilder.php @@ -12,6 +12,7 @@ interface PublicKeyCredentialCreationOptionsBuilder { public function getFromRequest( Request $request, - PublicKeyCredentialUserEntity $userEntity + PublicKeyCredentialUserEntity $userEntity, + /*bool $hideExistingExcludedCredentials = false*/ ): PublicKeyCredentialCreationOptions; } diff --git a/src/symfony/src/DependencyInjection/Configuration.php b/src/symfony/src/DependencyInjection/Configuration.php index e060a5ba8..b1031004a 100644 --- a/src/symfony/src/DependencyInjection/Configuration.php +++ b/src/symfony/src/DependencyInjection/Configuration.php @@ -354,6 +354,12 @@ private function addControllersConfig(ArrayNodeDefinition $rootNode): void ->scalarNode('user_entity_guesser') ->isRequired() ->end() + ->scalarNode('hide_existing_credentials') + ->info( + 'In order to prevent username enumeration, the existing credentials can be hidden. This is highly recommended when the attestation ceremony is performed by anonymous users.' + ) + ->defaultFalse() + ->end() ->scalarNode('options_storage') ->defaultValue(SessionStorage::class) ->info('Service responsible of the options/user entity storage during the ceremony') diff --git a/src/symfony/src/DependencyInjection/Factory/Security/WebauthnFactory.php b/src/symfony/src/DependencyInjection/Factory/Security/WebauthnFactory.php index 68eb78bec..7daae1b1b 100644 --- a/src/symfony/src/DependencyInjection/Factory/Security/WebauthnFactory.php +++ b/src/symfony/src/DependencyInjection/Factory/Security/WebauthnFactory.php @@ -415,6 +415,7 @@ private function createAttestationRequestControllerAndRoute( new Reference($optionsStorageId), new Reference($optionsHandlerId), new Reference($failureHandlerId), + true, ]); $this->createControllerAndRoute( $container, diff --git a/src/symfony/src/DependencyInjection/WebauthnExtension.php b/src/symfony/src/DependencyInjection/WebauthnExtension.php index 7240f9803..8c92df26b 100644 --- a/src/symfony/src/DependencyInjection/WebauthnExtension.php +++ b/src/symfony/src/DependencyInjection/WebauthnExtension.php @@ -215,6 +215,7 @@ private function loadCreationControllersSupport(ContainerBuilder $container, arr new Reference($creationConfig['options_storage']), new Reference($creationConfig['options_handler']), new Reference($creationConfig['failure_handler']), + $creationConfig['hide_existing_credentials'] ?? false, ]) ->addTag(DynamicRouteCompilerPass::TAG, [ 'method' => $creationConfig['options_method'], diff --git a/tests/symfony/config/config.yml b/tests/symfony/config/config.yml index e473f7d2d..d04ba5a3d 100644 --- a/tests/symfony/config/config.yml +++ b/tests/symfony/config/config.yml @@ -131,6 +131,7 @@ webauthn: enabled: true creation: test: + hide_existing_credentials: true options_path: '/devices/add/options' result_path: '/devices/add' #host: null diff --git a/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php b/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php index d2ed3912e..ef91ea582 100644 --- a/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php +++ b/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php @@ -64,6 +64,7 @@ public function anExistingUserCanAskForOptionsUsingTheDedicatedController(): voi static::assertArrayHasKey($expectedKey, $data); } static::assertSame('ok', $data['status']); + static::assertArrayNotHasKey('excludeCredentials', $data); // username enumeration prevention is enabled } #[Test] diff --git a/tests/symfony/functional/PublicKeyCredentialSourceRepository.php b/tests/symfony/functional/PublicKeyCredentialSourceRepository.php index ecb537691..570d007ac 100644 --- a/tests/symfony/functional/PublicKeyCredentialSourceRepository.php +++ b/tests/symfony/functional/PublicKeyCredentialSourceRepository.php @@ -38,6 +38,24 @@ public function __construct( 100 ); $this->saveCredentialSource($publicKeyCredentialSource1); + $publicKeyCredentialSource2 = PublicKeyCredentialSource::create( + base64_decode( + 'Ac8zKrpVWv9UCwxY1FyMqkESz2lV4CNwTk2+Hp19LgKbvh5uQ2/i6AMbTbTz1zcNapCEeiLJPlAAVM4L7AIow6I=', + true + ), + PublicKeyCredentialDescriptor::CREDENTIAL_TYPE_PUBLIC_KEY, + [], + AttestationStatement::TYPE_NONE, + EmptyTrustPath::create(), + Uuid::fromBinary(base64_decode('AAAAAAAAAAAAAAAAAAAAAA==', true)), + base64_decode( + 'pQECAyYgASFYIJV56vRrFusoDf9hm3iDmllcxxXzzKyO9WruKw4kWx7zIlgg/nq63l8IMJcIdKDJcXRh9hoz0L+nVwP1Oxil3/oNQYs=', + true + ), + '929fba2f-2361-4bc6-a917-bb76aa14c7f9', + 100 + ); + $this->saveCredentialSource($publicKeyCredentialSource2); } public function findOneByCredentialId(string $publicKeyCredentialId): ?PublicKeyCredentialSource From abd4ca4d0bfeb0ebd70b18971907481c667ef718 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sat, 6 Jul 2024 21:20:03 +0200 Subject: [PATCH 3/7] Add new denormalizers and deprecate old JSON methods (#618) * Add new denormalizers and deprecate old JSON methods Added new Denormalizer classes for AttestedCredentialData, AuthenticationExtensions, and others. Deprecated JSON serialization methods in several classes, scheduled to be removed in version 5.0. Now serialization relies fully on the Symfony Serializer component. Various minor amendments in other classes to support these changes. --- phpstan-baseline.neon | 87 +++++++++++++++++-- .../src/DataCollector/WebauthnCollector.php | 57 +++++++++--- .../src/Resources/config/dev_services.php | 2 + src/symfony/src/Resources/config/security.php | 6 +- src/symfony/src/Resources/config/services.php | 18 ++++ .../Handler/DefaultCreationOptionsHandler.php | 18 ++-- .../Handler/DefaultRequestOptionsHandler.php | 18 ++-- .../AttestationStatement.php | 6 ++ src/webauthn/src/AttestedCredentialData.php | 6 ++ .../AuthenticationExtension.php | 6 ++ .../AuthenticationExtensions.php | 6 ++ .../src/AuthenticatorSelectionCriteria.php | 6 ++ .../AttestedCredentialDataNormalizer.php | 42 +++++++++ .../AuthenticationExtensionNormalizer.php | 34 ++++++++ ...enticatorAssertionResponseDenormalizer.php | 4 +- ...ublicKeyCredentialDescriptorNormalizer.php | 44 ++++++++++ ...PublicKeyCredentialOptionsDenormalizer.php | 24 +++-- .../PublicKeyCredentialSourceDenormalizer.php | 35 +++++++- .../WebauthnSerializerFactory.php | 3 + src/webauthn/src/PublicKeyCredential.php | 2 +- .../PublicKeyCredentialCreationOptions.php | 6 ++ .../src/PublicKeyCredentialDescriptor.php | 6 ++ .../src/PublicKeyCredentialParameters.php | 6 ++ .../src/PublicKeyCredentialRequestOptions.php | 6 ++ .../src/PublicKeyCredentialRpEntity.php | 6 ++ .../src/PublicKeyCredentialSource.php | 10 ++- .../src/PublicKeyCredentialUserEntity.php | 6 ++ .../src/TrustPath/CertificateTrustPath.php | 6 ++ src/webauthn/src/TrustPath/EmptyTrustPath.php | 6 ++ .../src/TrustPath/TrustPathLoader.php | 27 +++--- tests/library/AbstractTestCase.php | 9 ++ tests/library/Functional/AssertionTest.php | 17 ++++ .../Unit/AttestedCredentialDataTest.php | 12 ++- ...ationExtensionsClientOutputsLoaderTest.php | 10 ++- .../AuthenticationExtensionsClientTest.php | 18 ++-- .../AuthenticatorSelectionCriteriaTest.php | 32 +++++-- tests/library/Unit/EntityTest.php | 17 ++-- ...PublicKeyCredentialCreationOptionsTest.php | 28 ++++-- .../PublicKeyCredentialDescriptorTest.php | 11 ++- .../PublicKeyCredentialParametersTest.php | 10 ++- .../PublicKeyCredentialRequestOptionsTest.php | 25 ++++-- .../Unit/PublicKeyCredentialSourceTest.php | 52 +++++++++-- .../library/Unit/TrustPath/TrustPathTest.php | 6 +- .../AdditionalAuthenticatorTest.php | 1 - .../Firewall/RegistrationAreaTest.php | 4 +- 45 files changed, 626 insertions(+), 135 deletions(-) create mode 100644 src/webauthn/src/Denormalizer/AttestedCredentialDataNormalizer.php create mode 100644 src/webauthn/src/Denormalizer/AuthenticationExtensionNormalizer.php create mode 100644 src/webauthn/src/Denormalizer/PublicKeyCredentialDescriptorNormalizer.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index bc26eb029..ac93686ad 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2421,6 +2421,41 @@ parameters: count: 1 path: src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AttestedCredentialDataNormalizer\\:\\:normalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AttestedCredentialDataNormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AttestedCredentialDataNormalizer\\:\\:normalize\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AttestedCredentialDataNormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AttestedCredentialDataNormalizer\\:\\:supportsNormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AttestedCredentialDataNormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticationExtensionNormalizer\\:\\:normalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticationExtensionNormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticationExtensionNormalizer\\:\\:normalize\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticationExtensionNormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticationExtensionNormalizer\\:\\:normalize\\(\\) should return array but returns mixed\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticationExtensionNormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticationExtensionNormalizer\\:\\:supportsNormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticationExtensionNormalizer.php + - message: """ #^Fetching class constant class of deprecated class Webauthn\\\\AuthenticationExtensions\\\\AuthenticationExtensionsClientInputs\\: @@ -2474,7 +2509,7 @@ parameters: - message: "#^Cannot access offset 'clientDataJSON' on mixed\\.$#" - count: 3 + count: 2 path: src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php - @@ -2484,7 +2519,7 @@ parameters: - message: "#^Cannot access offset 'userHandle' on mixed\\.$#" - count: 3 + count: 1 path: src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php - @@ -2522,11 +2557,6 @@ parameters: count: 1 path: src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php - - - message: "#^Parameter \\#4 \\$userHandle of static method Webauthn\\\\AuthenticatorAssertionResponse\\:\\:create\\(\\) expects string\\|null, mixed given\\.$#" - count: 1 - path: src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php - - message: "#^Parameter \\#5 \\$attestationObject of static method Webauthn\\\\AuthenticatorAssertionResponse\\:\\:create\\(\\) expects Webauthn\\\\AttestationStatement\\\\AttestationObject\\|null, mixed given\\.$#" count: 1 @@ -2687,6 +2717,21 @@ parameters: count: 1 path: src/webauthn/src/Denormalizer/PublicKeyCredentialDenormalizer.php + - + message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialDescriptorNormalizer\\:\\:normalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialDescriptorNormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialDescriptorNormalizer\\:\\:normalize\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialDescriptorNormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialDescriptorNormalizer\\:\\:supportsNormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialDescriptorNormalizer.php + - message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#" count: 1 @@ -2962,11 +3007,26 @@ parameters: count: 1 path: src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php + - + message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialSourceDenormalizer\\:\\:normalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialSourceDenormalizer\\:\\:normalize\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php + - message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialSourceDenormalizer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" count: 1 path: src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php + - + message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialSourceDenormalizer\\:\\:supportsNormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php + - message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\Util\\\\Base64\\:\\:decode\\(\\) expects string, mixed given\\.$#" count: 1 @@ -3292,3 +3352,16 @@ parameters: message: "#^Parameter \\#1 \\$ecdaaKeyId of class Webauthn\\\\TrustPath\\\\EcdaaKeyIdTrustPath constructor expects string, mixed given\\.$#" count: 1 path: src/webauthn/src/TrustPath/EcdaaKeyIdTrustPath.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/TrustPath/TrustPathLoader.php + + - + message: "#^Parameter \\#1 \\$certificates of static method Webauthn\\\\TrustPath\\\\CertificateTrustPath\\:\\:create\\(\\) expects array\\, array given\\.$#" + count: 1 + path: src/webauthn/src/TrustPath/TrustPathLoader.php diff --git a/src/symfony/src/DataCollector/WebauthnCollector.php b/src/symfony/src/DataCollector/WebauthnCollector.php index 3f4d3d73a..2f7c43668 100644 --- a/src/symfony/src/DataCollector/WebauthnCollector.php +++ b/src/symfony/src/DataCollector/WebauthnCollector.php @@ -8,6 +8,10 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\Serializer\Encoder\JsonEncode; +use Symfony\Component\Serializer\Encoder\JsonEncoder; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; +use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\VarCloner; use Throwable; @@ -52,6 +56,11 @@ class WebauthnCollector extends DataCollector implements EventSubscriberInterfac */ private array $authenticatorAssertionResponseValidationFailed = []; + public function __construct( + private readonly SerializerInterface $serializer + ) { + } + public function collect(Request $request, Response $response, ?Throwable $exception = null): void { $this->data = [ @@ -110,9 +119,13 @@ public function addPublicKeyCredentialCreationOptions(PublicKeyCredentialCreatio $cloner = new VarCloner(); $this->publicKeyCredentialCreationOptions[] = [ 'options' => $cloner->cloneVar($event->publicKeyCredentialCreationOptions), - 'json' => json_encode( + 'json' => $this->serializer->serialize( $event->publicKeyCredentialCreationOptions, - JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT + JsonEncoder::FORMAT, + [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + JsonEncode::OPTIONS => JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT, + ] ), ]; } @@ -124,9 +137,13 @@ public function addAuthenticatorAttestationResponseValidationSucceeded( $this->authenticatorAttestationResponseValidationSucceeded[] = [ 'attestation_response' => $cloner->cloneVar($event->authenticatorAttestationResponse), 'options' => $cloner->cloneVar($event->publicKeyCredentialCreationOptions), - 'options_json' => json_encode( + 'options_json' => $this->serializer->serialize( $event->publicKeyCredentialCreationOptions, - JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT + JsonEncoder::FORMAT, + [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + JsonEncode::OPTIONS => JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT, + ] ), 'credential_source' => $cloner->cloneVar($event->publicKeyCredentialSource), ]; @@ -139,9 +156,13 @@ public function addAuthenticatorAttestationResponseValidationFailed( $this->authenticatorAttestationResponseValidationFailed[] = [ 'attestation_response' => $cloner->cloneVar($event->authenticatorAttestationResponse), 'options' => $cloner->cloneVar($event->publicKeyCredentialCreationOptions), - 'options_json' => json_encode( + 'options_json' => $this->serializer->serialize( $event->publicKeyCredentialCreationOptions, - JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT + JsonEncoder::FORMAT, + [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + JsonEncode::OPTIONS => JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT, + ] ), 'exception' => $cloner->cloneVar($event->throwable), ]; @@ -152,9 +173,13 @@ public function addPublicKeyCredentialRequestOptions(PublicKeyCredentialRequestO $cloner = new VarCloner(); $this->publicKeyCredentialRequestOptions[] = [ 'options' => $cloner->cloneVar($event->publicKeyCredentialRequestOptions), - 'json' => json_encode( + 'json' => $this->serializer->serialize( $event->publicKeyCredentialRequestOptions, - JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT + JsonEncoder::FORMAT, + [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + JsonEncode::OPTIONS => JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT, + ] ), ]; } @@ -168,9 +193,13 @@ public function addAuthenticatorAssertionResponseValidationSucceeded( 'credential_id' => $cloner->cloneVar($event->credentialId), 'assertion_response' => $cloner->cloneVar($event->authenticatorAssertionResponse), 'options' => $cloner->cloneVar($event->publicKeyCredentialRequestOptions), - 'options_json' => json_encode( + 'options_json' => $this->serializer->serialize( $event->publicKeyCredentialRequestOptions, - JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT + JsonEncoder::FORMAT, + [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + JsonEncode::OPTIONS => JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT, + ] ), 'credential_source' => $cloner->cloneVar($event->publicKeyCredentialSource), ]; @@ -185,9 +214,13 @@ public function addAuthenticatorAssertionResponseValidationFailed( 'credential_id' => $cloner->cloneVar($event->getCredential()?->publicKeyCredentialId), 'assertion_response' => $cloner->cloneVar($event->authenticatorAssertionResponse), 'options' => $cloner->cloneVar($event->publicKeyCredentialRequestOptions), - 'options_json' => json_encode( + 'options_json' => $this->serializer->serialize( $event->publicKeyCredentialRequestOptions, - JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT + JsonEncoder::FORMAT, + [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + JsonEncode::OPTIONS => JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT, + ] ), 'exception' => $cloner->cloneVar($event->throwable), ]; diff --git a/src/symfony/src/Resources/config/dev_services.php b/src/symfony/src/Resources/config/dev_services.php index a472ffcd6..2833b492e 100644 --- a/src/symfony/src/Resources/config/dev_services.php +++ b/src/symfony/src/Resources/config/dev_services.php @@ -5,6 +5,7 @@ namespace Webauthn\Bundle\DataCollector; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use function Symfony\Component\DependencyInjection\Loader\Configurator\service; return static function (ContainerConfigurator $container): void { $container = $container->services() @@ -13,6 +14,7 @@ ->autoconfigure(); $container->set(WebauthnCollector::class) + ->args([service('serializer')]) ->tag('data_collector', [ 'id' => 'webauthn_collector', 'template' => '@Webauthn/data_collector/template.html.twig', diff --git a/src/symfony/src/Resources/config/security.php b/src/symfony/src/Resources/config/security.php index 3e7c2ce10..2f664f754 100644 --- a/src/symfony/src/Resources/config/security.php +++ b/src/symfony/src/Resources/config/security.php @@ -36,8 +36,10 @@ $container->set(DefaultFailureHandler::class); $container->set(SessionStorage::class)->args([service('request_stack')]); $container->set(CacheStorage::class)->args([service(CacheItemPoolInterface::class)]); - $container->set(DefaultCreationOptionsHandler::class); - $container->set(DefaultRequestOptionsHandler::class); + $container->set(DefaultCreationOptionsHandler::class) + ->args([service('serializer')]); + $container->set(DefaultRequestOptionsHandler::class) + ->args([service('serializer')]); $container ->set(WebauthnFactory::AUTHENTICATOR_DEFINITION_ID, WebauthnAuthenticator::class) ->abstract() diff --git a/src/symfony/src/Resources/config/services.php b/src/symfony/src/Resources/config/services.php index 45e939412..7f337d611 100644 --- a/src/symfony/src/Resources/config/services.php +++ b/src/symfony/src/Resources/config/services.php @@ -32,6 +32,8 @@ use Webauthn\Counter\ThrowExceptionIfInvalid; use Webauthn\Denormalizer\AttestationObjectDenormalizer; use Webauthn\Denormalizer\AttestationStatementDenormalizer; +use Webauthn\Denormalizer\AttestedCredentialDataNormalizer; +use Webauthn\Denormalizer\AuthenticationExtensionNormalizer; use Webauthn\Denormalizer\AuthenticationExtensionsDenormalizer; use Webauthn\Denormalizer\AuthenticatorAssertionResponseDenormalizer; use Webauthn\Denormalizer\AuthenticatorAttestationResponseDenormalizer; @@ -39,6 +41,7 @@ use Webauthn\Denormalizer\AuthenticatorResponseDenormalizer; use Webauthn\Denormalizer\CollectedClientDataDenormalizer; use Webauthn\Denormalizer\PublicKeyCredentialDenormalizer; +use Webauthn\Denormalizer\PublicKeyCredentialDescriptorNormalizer; use Webauthn\Denormalizer\PublicKeyCredentialOptionsDenormalizer; use Webauthn\Denormalizer\PublicKeyCredentialSourceDenormalizer; use Webauthn\Denormalizer\PublicKeyCredentialUserEntityDenormalizer; @@ -209,6 +212,21 @@ ->tag('serializer.normalizer', [ 'priority' => 1024, ]); + $container + ->set(AuthenticationExtensionNormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(PublicKeyCredentialDescriptorNormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); + $container + ->set(AttestedCredentialDataNormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); $container ->set(AuthenticationExtensionsDenormalizer::class) ->tag('serializer.normalizer', [ diff --git a/src/symfony/src/Security/Handler/DefaultCreationOptionsHandler.php b/src/symfony/src/Security/Handler/DefaultCreationOptionsHandler.php index b4e5a0c9e..6a8fdb931 100644 --- a/src/symfony/src/Security/Handler/DefaultCreationOptionsHandler.php +++ b/src/symfony/src/Security/Handler/DefaultCreationOptionsHandler.php @@ -7,23 +7,27 @@ use RuntimeException; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Serializer\Encoder\JsonEncoder; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Webauthn\PublicKeyCredentialCreationOptions; use Webauthn\PublicKeyCredentialUserEntity; use function is_array; -use const JSON_THROW_ON_ERROR; final class DefaultCreationOptionsHandler implements CreationOptionsHandler { + public function __construct( + private readonly NormalizerInterface $normalizer + ) { + } + public function onCreationOptions( PublicKeyCredentialCreationOptions $publicKeyCredentialCreationOptions, PublicKeyCredentialUserEntity $userEntity ): Response { - $data = json_decode( - json_encode($publicKeyCredentialCreationOptions, JSON_THROW_ON_ERROR), - true, - 512, - JSON_THROW_ON_ERROR - ); + $data = $this->normalizer->normalize($publicKeyCredentialCreationOptions, JsonEncoder::FORMAT, [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]); is_array($data) || throw new RuntimeException('Unable to encode the response to JSON.'); $data['status'] = 'ok'; $data['errorMessage'] = ''; diff --git a/src/symfony/src/Security/Handler/DefaultRequestOptionsHandler.php b/src/symfony/src/Security/Handler/DefaultRequestOptionsHandler.php index 3fdbad469..3c790e8e1 100644 --- a/src/symfony/src/Security/Handler/DefaultRequestOptionsHandler.php +++ b/src/symfony/src/Security/Handler/DefaultRequestOptionsHandler.php @@ -7,23 +7,27 @@ use RuntimeException; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Serializer\Encoder\JsonEncoder; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Webauthn\PublicKeyCredentialRequestOptions; use Webauthn\PublicKeyCredentialUserEntity; use function is_array; -use const JSON_THROW_ON_ERROR; final class DefaultRequestOptionsHandler implements RequestOptionsHandler { + public function __construct( + private readonly NormalizerInterface $normalizer + ) { + } + public function onRequestOptions( PublicKeyCredentialRequestOptions $publicKeyCredentialRequestOptions, ?PublicKeyCredentialUserEntity $userEntity ): Response { - $data = json_decode( - json_encode($publicKeyCredentialRequestOptions, JSON_THROW_ON_ERROR), - true, - 512, - JSON_THROW_ON_ERROR - ); + $data = $this->normalizer->normalize($publicKeyCredentialRequestOptions, JsonEncoder::FORMAT, [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]); is_array($data) || throw new RuntimeException('Unable to encode the response to JSON.'); $data['status'] = 'ok'; $data['errorMessage'] = ''; diff --git a/src/webauthn/src/AttestationStatement/AttestationStatement.php b/src/webauthn/src/AttestationStatement/AttestationStatement.php index 6f216783f..4f3004478 100644 --- a/src/webauthn/src/AttestationStatement/AttestationStatement.php +++ b/src/webauthn/src/AttestationStatement/AttestationStatement.php @@ -174,6 +174,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); return [ 'fmt' => $this->fmt, 'attStmt' => $this->attStmt, diff --git a/src/webauthn/src/AttestedCredentialData.php b/src/webauthn/src/AttestedCredentialData.php index a0d589d23..2f5d74f41 100644 --- a/src/webauthn/src/AttestedCredentialData.php +++ b/src/webauthn/src/AttestedCredentialData.php @@ -108,6 +108,12 @@ public static function createFromArray(array $json): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); $result = [ 'aaguid' => $this->aaguid->__toString(), 'credentialId' => base64_encode($this->credentialId), diff --git a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtension.php b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtension.php index 3f9ff6a2b..3ed9dc0c9 100644 --- a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtension.php +++ b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtension.php @@ -39,6 +39,12 @@ public function value(): mixed public function jsonSerialize(): mixed { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); return $this->value; } } diff --git a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensions.php b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensions.php index bedabbb53..9d7b2bedb 100644 --- a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensions.php +++ b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensions.php @@ -109,6 +109,12 @@ public function get(string $key): AuthenticationExtension */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); return $this->extensions; } diff --git a/src/webauthn/src/AuthenticatorSelectionCriteria.php b/src/webauthn/src/AuthenticatorSelectionCriteria.php index 0bbad86fd..8e47e92ee 100644 --- a/src/webauthn/src/AuthenticatorSelectionCriteria.php +++ b/src/webauthn/src/AuthenticatorSelectionCriteria.php @@ -229,6 +229,12 @@ public static function createFromArray(array $json): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); $json = [ 'requireResidentKey' => $this->requireResidentKey, 'userVerification' => $this->userVerification, diff --git a/src/webauthn/src/Denormalizer/AttestedCredentialDataNormalizer.php b/src/webauthn/src/Denormalizer/AttestedCredentialDataNormalizer.php new file mode 100644 index 000000000..b46c57c34 --- /dev/null +++ b/src/webauthn/src/Denormalizer/AttestedCredentialDataNormalizer.php @@ -0,0 +1,42 @@ + $this->normalizer->normalize($data->aaguid, $format, $context), + 'credentialId' => base64_encode($data->credentialId), + ]; + if ($data->credentialPublicKey !== null) { + $result['credentialPublicKey'] = base64_encode($data->credentialPublicKey); + } + + return $result; + } + + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool + { + return $data instanceof AttestedCredentialData; + } + + public function getSupportedTypes(?string $format): array + { + return [ + AttestedCredentialData::class => true, + ]; + } +} diff --git a/src/webauthn/src/Denormalizer/AuthenticationExtensionNormalizer.php b/src/webauthn/src/Denormalizer/AuthenticationExtensionNormalizer.php new file mode 100644 index 000000000..26102f558 --- /dev/null +++ b/src/webauthn/src/Denormalizer/AuthenticationExtensionNormalizer.php @@ -0,0 +1,34 @@ + + */ + public function getSupportedTypes(?string $format): array + { + return [ + AuthenticationExtension::class => true, + ]; + } + + public function normalize(mixed $data, ?string $format = null, array $context = []): array + { + assert($data instanceof AuthenticationExtension); + + return $data->value; + } + + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool + { + return $data instanceof AuthenticationExtension; + } +} diff --git a/src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php b/src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php index 1f017ca21..613331c7a 100644 --- a/src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php +++ b/src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php @@ -25,14 +25,14 @@ public function denormalize(mixed $data, string $type, string $format = null, ar $data['clientDataJSON'] = Base64UrlSafe::decodeNoPadding($data['clientDataJSON']); $userHandle = $data['userHandle'] ?? null; if ($userHandle !== '' && $userHandle !== null) { - $data['userHandle'] = Base64::decode($userHandle); + $userHandle = Base64::decode($userHandle); } return AuthenticatorAssertionResponse::create( $this->denormalizer->denormalize($data['clientDataJSON'], CollectedClientData::class, $format, $context), $this->denormalizer->denormalize($data['authenticatorData'], AuthenticatorData::class, $format, $context), $data['signature'], - $data['userHandle'] ?? null, + $userHandle ?? null, ! isset($data['attestationObject']) ? null : $this->denormalizer->denormalize( $data['attestationObject'], AttestationObject::class, diff --git a/src/webauthn/src/Denormalizer/PublicKeyCredentialDescriptorNormalizer.php b/src/webauthn/src/Denormalizer/PublicKeyCredentialDescriptorNormalizer.php new file mode 100644 index 000000000..2accc3075 --- /dev/null +++ b/src/webauthn/src/Denormalizer/PublicKeyCredentialDescriptorNormalizer.php @@ -0,0 +1,44 @@ + $data->type, + 'id' => Base64UrlSafe::encodeUnpadded($data->id), + ]; + if (count($data->transports) !== 0) { + $result['transports'] = $data->transports; + } + + return $result; + } + + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool + { + return $data instanceof PublicKeyCredentialDescriptor; + } + + public function getSupportedTypes(?string $format): array + { + return [ + PublicKeyCredentialDescriptor::class => true, + ]; + } +} diff --git a/src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php b/src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php index fc35aff64..06657613a 100644 --- a/src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php +++ b/src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php @@ -136,14 +136,18 @@ public function normalize(mixed $data, ?string $format = null, array $context = $json = [ 'challenge' => Base64UrlSafe::encodeUnpadded($data->challenge), 'timeout' => $data->timeout, - 'extensions' => $this->normalizer->normalize($data->extensions, $format, $context), + 'extensions' => $data->extensions->count() === 0 ? null : $this->normalizer->normalize( + $data->extensions, + $format, + $context + ), ]; if ($data instanceof PublicKeyCredentialCreationOptions) { $json = [ ...$json, - 'rp' => $this->normalizer->normalize($data->rp, PublicKeyCredentialRpEntity::class, $context), - 'user' => $this->normalizer->normalize($data->user, PublicKeyCredentialUserEntity::class, $context), + 'rp' => $this->normalizer->normalize($data->rp, $format, $context), + 'user' => $this->normalizer->normalize($data->user, $format, $context), 'pubKeyCredParams' => $this->normalizer->normalize( $data->pubKeyCredParams, PublicKeyCredentialParameters::class . '[]', @@ -151,26 +155,18 @@ public function normalize(mixed $data, ?string $format = null, array $context = ), 'authenticatorSelection' => $data->authenticatorSelection === null ? null : $this->normalizer->normalize( $data->authenticatorSelection, - AuthenticatorSelectionCriteria::class, + $format, $context ), 'attestation' => $data->attestation, - 'excludeCredentials' => $this->normalizer->normalize( - $data->excludeCredentials, - PublicKeyCredentialDescriptor::class . '[]', - $context - ), + 'excludeCredentials' => $this->normalizer->normalize($data->excludeCredentials, $format, $context), ]; } if ($data instanceof PublicKeyCredentialRequestOptions) { $json = [ ...$json, 'rpId' => $data->rpId, - 'allowCredentials' => $this->normalizer->normalize( - $data->allowCredentials, - PublicKeyCredentialDescriptor::class . '[]', - $context - ), + 'allowCredentials' => $this->normalizer->normalize($data->allowCredentials, $format, $context), 'userVerification' => $data->userVerification, ]; } diff --git a/src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php b/src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php index df5992485..51018d8f5 100644 --- a/src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php +++ b/src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php @@ -4,18 +4,24 @@ namespace Webauthn\Denormalizer; +use ParagonIE\ConstantTime\Base64UrlSafe; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Uid\Uuid; use Webauthn\Exception\InvalidDataException; use Webauthn\PublicKeyCredentialSource; use Webauthn\TrustPath\TrustPath; use Webauthn\Util\Base64; use function array_key_exists; +use function assert; -final class PublicKeyCredentialSourceDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface +final class PublicKeyCredentialSourceDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface, NormalizerInterface, NormalizerAwareInterface { + use NormalizerAwareTrait; use DenormalizerAwareTrait; public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed @@ -57,4 +63,31 @@ public function getSupportedTypes(?string $format): array PublicKeyCredentialSource::class => true, ]; } + + public function normalize(mixed $data, ?string $format = null, array $context = []): array + { + assert($data instanceof PublicKeyCredentialSource); + $result = [ + 'publicKeyCredentialId' => Base64UrlSafe::encodeUnpadded($data->publicKeyCredentialId), + 'type' => $data->type, + 'transports' => $data->transports, + 'attestationType' => $data->attestationType, + 'trustPath' => $this->normalizer->normalize($data->trustPath, $format, $context), + 'aaguid' => $this->normalizer->normalize($data->aaguid, $format, $context), + 'credentialPublicKey' => Base64UrlSafe::encodeUnpadded($data->credentialPublicKey), + 'userHandle' => Base64UrlSafe::encodeUnpadded($data->userHandle), + 'counter' => $data->counter, + 'otherUI' => $data->otherUI, + 'backupEligible' => $data->backupEligible, + 'backupStatus' => $data->backupStatus, + 'uvInitialized' => $data->uvInitialized, + ]; + + return array_filter($result, static fn ($value): bool => $value !== null); + } + + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool + { + return $data instanceof PublicKeyCredentialSource; + } } diff --git a/src/webauthn/src/Denormalizer/WebauthnSerializerFactory.php b/src/webauthn/src/Denormalizer/WebauthnSerializerFactory.php index 304cd035b..694039a01 100644 --- a/src/webauthn/src/Denormalizer/WebauthnSerializerFactory.php +++ b/src/webauthn/src/Denormalizer/WebauthnSerializerFactory.php @@ -42,6 +42,9 @@ public function create(): SerializerInterface } $denormalizers = [ + new AuthenticationExtensionNormalizer(), + new PublicKeyCredentialDescriptorNormalizer(), + new AttestedCredentialDataNormalizer(), new AttestationObjectDenormalizer(), new AttestationStatementDenormalizer($this->attestationStatementSupportManager), new AuthenticationExtensionsDenormalizer(), diff --git a/src/webauthn/src/PublicKeyCredential.php b/src/webauthn/src/PublicKeyCredential.php index 778d138b9..8e09ad656 100644 --- a/src/webauthn/src/PublicKeyCredential.php +++ b/src/webauthn/src/PublicKeyCredential.php @@ -23,7 +23,7 @@ public function __construct( } /** - * @deprecated since 4.8.0. Please use the PublicKeyCredentialDescriptor ({self::getPublicKeyCredentialDescriptor}) instead. + * @deprecated since 4.8.0. * @infection-ignore-all */ public function __toString(): string diff --git a/src/webauthn/src/PublicKeyCredentialCreationOptions.php b/src/webauthn/src/PublicKeyCredentialCreationOptions.php index 1a20ee5ee..d6088e97a 100644 --- a/src/webauthn/src/PublicKeyCredentialCreationOptions.php +++ b/src/webauthn/src/PublicKeyCredentialCreationOptions.php @@ -313,6 +313,12 @@ public static function createFromArray(array $json): static */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); $json = [ 'rp' => $this->rp, 'user' => $this->user, diff --git a/src/webauthn/src/PublicKeyCredentialDescriptor.php b/src/webauthn/src/PublicKeyCredentialDescriptor.php index ba38446e8..eac256903 100644 --- a/src/webauthn/src/PublicKeyCredentialDescriptor.php +++ b/src/webauthn/src/PublicKeyCredentialDescriptor.php @@ -107,6 +107,12 @@ public static function createFromArray(array $json): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); $json = [ 'type' => $this->type, 'id' => Base64UrlSafe::encodeUnpadded($this->id), diff --git a/src/webauthn/src/PublicKeyCredentialParameters.php b/src/webauthn/src/PublicKeyCredentialParameters.php index 62cfa0534..1eab10d57 100644 --- a/src/webauthn/src/PublicKeyCredentialParameters.php +++ b/src/webauthn/src/PublicKeyCredentialParameters.php @@ -83,6 +83,12 @@ public static function createFromArray(array $json): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); return [ 'type' => $this->type, 'alg' => $this->alg, diff --git a/src/webauthn/src/PublicKeyCredentialRequestOptions.php b/src/webauthn/src/PublicKeyCredentialRequestOptions.php index 4016f34f6..a74d97b38 100644 --- a/src/webauthn/src/PublicKeyCredentialRequestOptions.php +++ b/src/webauthn/src/PublicKeyCredentialRequestOptions.php @@ -204,6 +204,12 @@ public static function createFromArray(array $json): static */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); $json = [ 'challenge' => Base64UrlSafe::encodeUnpadded($this->challenge), ]; diff --git a/src/webauthn/src/PublicKeyCredentialRpEntity.php b/src/webauthn/src/PublicKeyCredentialRpEntity.php index 1720462b9..bb40b5395 100644 --- a/src/webauthn/src/PublicKeyCredentialRpEntity.php +++ b/src/webauthn/src/PublicKeyCredentialRpEntity.php @@ -51,6 +51,12 @@ public static function createFromArray(array $json): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); $json = parent::jsonSerialize(); if ($this->id !== null) { $json['id'] = $this->id; diff --git a/src/webauthn/src/PublicKeyCredentialSource.php b/src/webauthn/src/PublicKeyCredentialSource.php index de55bb104..ca94d8a5c 100644 --- a/src/webauthn/src/PublicKeyCredentialSource.php +++ b/src/webauthn/src/PublicKeyCredentialSource.php @@ -248,7 +248,13 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { - return [ + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); + $result = [ 'publicKeyCredentialId' => Base64UrlSafe::encodeUnpadded($this->publicKeyCredentialId), 'type' => $this->type, 'transports' => $this->transports, @@ -263,5 +269,7 @@ public function jsonSerialize(): array 'backupStatus' => $this->backupStatus, 'uvInitialized' => $this->uvInitialized, ]; + + return array_filter($result, static fn ($value): bool => $value !== null); } } diff --git a/src/webauthn/src/PublicKeyCredentialUserEntity.php b/src/webauthn/src/PublicKeyCredentialUserEntity.php index 53f147d8c..b5c75ee3f 100644 --- a/src/webauthn/src/PublicKeyCredentialUserEntity.php +++ b/src/webauthn/src/PublicKeyCredentialUserEntity.php @@ -87,6 +87,12 @@ public static function createFromArray(array $json): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); $json = parent::jsonSerialize(); $json['id'] = Base64UrlSafe::encodeUnpadded($this->id); $json['displayName'] = $this->displayName; diff --git a/src/webauthn/src/TrustPath/CertificateTrustPath.php b/src/webauthn/src/TrustPath/CertificateTrustPath.php index 796511c50..5bc39fbf6 100644 --- a/src/webauthn/src/TrustPath/CertificateTrustPath.php +++ b/src/webauthn/src/TrustPath/CertificateTrustPath.php @@ -56,6 +56,12 @@ public static function createFromArray(array $data): static */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); return [ 'type' => self::class, 'x5c' => $this->certificates, diff --git a/src/webauthn/src/TrustPath/EmptyTrustPath.php b/src/webauthn/src/TrustPath/EmptyTrustPath.php index 744103364..a91a6121d 100644 --- a/src/webauthn/src/TrustPath/EmptyTrustPath.php +++ b/src/webauthn/src/TrustPath/EmptyTrustPath.php @@ -16,6 +16,12 @@ public static function create(): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + __METHOD__ + ); return [ 'type' => self::class, ]; diff --git a/src/webauthn/src/TrustPath/TrustPathLoader.php b/src/webauthn/src/TrustPath/TrustPathLoader.php index e06b1551c..b79675bc0 100644 --- a/src/webauthn/src/TrustPath/TrustPathLoader.php +++ b/src/webauthn/src/TrustPath/TrustPathLoader.php @@ -6,8 +6,8 @@ use Webauthn\Exception\InvalidTrustPathException; use function array_key_exists; -use function class_implements; -use function in_array; +use function is_array; +use function is_string; final class TrustPathLoader { @@ -16,18 +16,15 @@ final class TrustPathLoader */ public static function loadTrustPath(array $data): TrustPath { - array_key_exists('type', $data) || throw InvalidTrustPathException::create('The trust path type is missing'); - $type = $data['type']; - if (class_exists($type) !== true) { - throw InvalidTrustPathException::create( - sprintf('The trust path type "%s" is not supported', $data['type']) - ); - } - - $implements = class_implements($type); - if (in_array(TrustPath::class, $implements, true)) { - return $type::createFromArray($data); - } - throw InvalidTrustPathException::create(sprintf('The trust path type "%s" is not supported', $data['type'])); + return match (true) { + $data === [] || $data === [ + 'type' => EmptyTrustPath::class, + ] => EmptyTrustPath::create(), + array_key_exists('x5c', $data) && is_array($data['x5c']) => CertificateTrustPath::create($data['x5c']), + array_key_exists('ecdaaKeyId', $data) && is_string($data['ecdaaKeyId']) => new EcdaaKeyIdTrustPath( + $data['ecdaaKeyId'] + ), + default => throw InvalidTrustPathException::create('Unsupported trust path'), + }; } } diff --git a/tests/library/AbstractTestCase.php b/tests/library/AbstractTestCase.php index f90b29289..48fc750e3 100644 --- a/tests/library/AbstractTestCase.php +++ b/tests/library/AbstractTestCase.php @@ -20,6 +20,7 @@ use Webauthn\AttestationStatement\AndroidKeyAttestationStatementSupport; use Webauthn\AttestationStatement\AndroidSafetyNetAttestationStatementSupport; use Webauthn\AttestationStatement\AppleAttestationStatementSupport; +use Webauthn\AttestationStatement\AttestationObjectLoader; use Webauthn\AttestationStatement\AttestationStatementSupportManager; use Webauthn\AttestationStatement\FidoU2FAttestationStatementSupport; use Webauthn\AttestationStatement\NoneAttestationStatementSupport; @@ -36,6 +37,7 @@ use Webauthn\MetadataService\Service\ChainedMetadataServices; use Webauthn\MetadataService\Service\JsonMetadataService; use Webauthn\MetadataService\Service\LocalResourceMetadataService; +use Webauthn\PublicKeyCredentialLoader; use Webauthn\Tests\Bundle\Functional\MockClock; use Webauthn\Tests\Functional\MetadataStatementRepository; use Webauthn\Tests\Functional\StatusReportRepository; @@ -152,6 +154,13 @@ protected function getSerializer(): SerializerInterface return $this->webauthnSerializer; } + protected function getLegacyLoader(): PublicKeyCredentialLoader + { + return new PublicKeyCredentialLoader(new AttestationObjectLoader( + $this->getAttestationStatementSupportManager() + )); + } + private function getAttestationStatementSupportManager(): AttestationStatementSupportManager { $attestationStatementSupportManager = new AttestationStatementSupportManager(); diff --git a/tests/library/Functional/AssertionTest.php b/tests/library/Functional/AssertionTest.php index 605ffc746..62e392b41 100644 --- a/tests/library/Functional/AssertionTest.php +++ b/tests/library/Functional/AssertionTest.php @@ -69,6 +69,23 @@ public function anAssertionCanBeVerified(): void static::assertSame(123, $publicKeyCredentialSource->counter); } + #[Test] + public function serializerAndLegacyMethodGiveTheSameResult(): void + { + // Given + $input = '{"id":"ADqYfFWXiscOCOPCd9OLiBtSGhletNPKlSOELS0Nuwj_uCzf9s3trLUK9ockO8xa8jBAYdKixLZYOAezy0FJiV1bnTCty_LiInWWJlov","type":"public-key","rawId":"ADqYfFWXiscOCOPCd9OLiBtSGhletNPKlSOELS0Nuwj/uCzf9s3trLUK9ockO8xa8jBAYdKixLZYOAezy0FJiV1bnTCty/LiInWWJlov","response":{"authenticatorData":"tIXbbgSILsWHHbR0Fjkl96X4ROZYLvVtOopBWCQoAqpFXFBJyQAAAAAAAAAAAAAAAAAAAAAATgA6mHxVl4rHDgjjwnfTi4gbUhoZXrTTypUjhC0tDbsI_7gs3_bN7ay1CvaHJDvMWvIwQGHSosS2WDgHs8tBSYldW50wrcvy4iJ1liZaL6UBAgMmIAEhWCAIpUDJSoLScguLRDKBEc32v682i6RPjy6SFZnFTBj2QSJYIG8DS0CpphjyFyZB9xyCTrKDsr_S5iX5hhidWLRdP_7B","clientDataJSON":"eyJjaGFsbGVuZ2UiOiJ3S2xXN1MzRUVOSGxjRjJOZ1loZFVKZlJKZUN2QXZsYmstTWxsdnhvMEhBIiwib3JpZ2luIjoiaHR0cHM6Ly9zcG9ta3ktd2ViYXV0aG4uaGVyb2t1YXBwLmNvbSIsInR5cGUiOiJ3ZWJhdXRobi5nZXQifQ","signature":"MEQCIBnVPX8inAXIxXAsMdF6nW6nZJa36G1O+G9JXiauenxBAiBU4MQoRWxiXGn0TcKTkRJafZ58KLqeCJiB2VFAplwPJA==","userHandle":"YWJmYzhmZGYtMDdmNi00NWE5LWFiZWMtZmExOTIyNzViMjc2"}}'; + + //When + $publicKeyCredential1 = $this->getSerializer() + ->deserialize($input, PublicKeyCredential::class, 'json'); + $publicKeyCredential2 = $this->getLegacyLoader() + ->load($input); + + //Then + static::assertEquals($publicKeyCredential1, $publicKeyCredential2); + + } + #[Test] public function anAssertionWithTokenBindingCanBeVerified(): void { diff --git a/tests/library/Unit/AttestedCredentialDataTest.php b/tests/library/Unit/AttestedCredentialDataTest.php index 6a863a65b..be8a13efd 100644 --- a/tests/library/Unit/AttestedCredentialDataTest.php +++ b/tests/library/Unit/AttestedCredentialDataTest.php @@ -6,20 +6,21 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Uid\Uuid; use Webauthn\AttestedCredentialData; -use const JSON_UNESCAPED_SLASHES; +use Webauthn\Tests\AbstractTestCase; /** * @internal */ -final class AttestedCredentialDataTest extends TestCase +final class AttestedCredentialDataTest extends AbstractTestCase { #[Test] #[DataProvider('dataAAGUID')] public function anAttestedCredentialDataCanBeCreatedAndValueAccessed(string $uuid): void { + // Given $attestedCredentialData = AttestedCredentialData::create(Uuid::fromString( $uuid ), 'credential_id', 'credential_public_key'); @@ -29,7 +30,10 @@ public function anAttestedCredentialDataCanBeCreatedAndValueAccessed(string $uui static::assertSame('credential_public_key', $attestedCredentialData->credentialPublicKey); static::assertSame( '{"aaguid":"' . $uuid . '","credentialId":"Y3JlZGVudGlhbF9pZA==","credentialPublicKey":"Y3JlZGVudGlhbF9wdWJsaWNfa2V5"}', - json_encode($attestedCredentialData, JSON_UNESCAPED_SLASHES) + $this->getSerializer() + ->serialize($attestedCredentialData, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]) ); json_decode( diff --git a/tests/library/Unit/AuthenticationExtensions/AuthenticationExtensionsClientOutputsLoaderTest.php b/tests/library/Unit/AuthenticationExtensions/AuthenticationExtensionsClientOutputsLoaderTest.php index 1fb97af20..1ea8e2cd9 100644 --- a/tests/library/Unit/AuthenticationExtensions/AuthenticationExtensionsClientOutputsLoaderTest.php +++ b/tests/library/Unit/AuthenticationExtensions/AuthenticationExtensionsClientOutputsLoaderTest.php @@ -9,16 +9,16 @@ use CBOR\MapObject; use CBOR\OtherObject\TrueObject; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Webauthn\AuthenticationExtensions\AuthenticationExtensionsClientOutputs; use Webauthn\AuthenticationExtensions\AuthenticationExtensionsClientOutputsLoader; use Webauthn\Exception\AuthenticationExtensionException; -use const JSON_THROW_ON_ERROR; +use Webauthn\Tests\AbstractTestCase; /** * @internal */ -final class AuthenticationExtensionsClientOutputsLoaderTest extends TestCase +final class AuthenticationExtensionsClientOutputsLoaderTest extends AbstractTestCase { #[Test] public function theExtensionsCanBeLoaded(): void @@ -29,7 +29,9 @@ public function theExtensionsCanBeLoaded(): void static::assertInstanceOf(AuthenticationExtensionsClientOutputs::class, $extensions); static::assertCount(1, $extensions); - static::assertSame('{"loc":true}', json_encode($extensions, JSON_THROW_ON_ERROR)); + static::assertSame('{"loc":true}', $this->getSerializer()->serialize($extensions, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); } #[Test] diff --git a/tests/library/Unit/AuthenticationExtensions/AuthenticationExtensionsClientTest.php b/tests/library/Unit/AuthenticationExtensions/AuthenticationExtensionsClientTest.php index 18ad55d6e..dc8118f74 100644 --- a/tests/library/Unit/AuthenticationExtensions/AuthenticationExtensionsClientTest.php +++ b/tests/library/Unit/AuthenticationExtensions/AuthenticationExtensionsClientTest.php @@ -5,16 +5,16 @@ namespace Webauthn\Tests\Unit\AuthenticationExtensions; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Webauthn\AuthenticationExtensions\AuthenticationExtension; use Webauthn\AuthenticationExtensions\AuthenticationExtensionsClientInputs; use Webauthn\AuthenticationExtensions\AuthenticationExtensionsClientOutputs; -use const JSON_THROW_ON_ERROR; +use Webauthn\Tests\AbstractTestCase; /** * @internal */ -final class AuthenticationExtensionsClientTest extends TestCase +final class AuthenticationExtensionsClientTest extends AbstractTestCase { #[Test] public function anAuthenticationExtensionsClientCanBeCreatedAndValueAccessed(): void @@ -23,7 +23,9 @@ public function anAuthenticationExtensionsClientCanBeCreatedAndValueAccessed(): static::assertSame('name', $extension->name); static::assertSame(['value'], $extension->value); - static::assertSame('["value"]', json_encode($extension, JSON_THROW_ON_ERROR)); + static::assertSame('["value"]', $this->getSerializer()->serialize($extension, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); } #[Test] @@ -34,7 +36,9 @@ public function theAuthenticationExtensionsClientInputsCanManageExtensions(): vo ]); static::assertSame(1, $inputs->count()); - static::assertSame('{"name":["value"]}', json_encode($inputs, JSON_THROW_ON_ERROR)); + static::assertSame('{"name":["value"]}', $this->getSerializer()->serialize($inputs, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); static::assertContainsOnlyInstancesOf(AuthenticationExtension::class, $inputs); } @@ -46,7 +50,9 @@ public function theAuthenticationExtensionsClientOutputsCanManageExtensions(): v ]); static::assertSame(1, $inputs->count()); - static::assertSame('{"name":["value"]}', json_encode($inputs, JSON_THROW_ON_ERROR)); + static::assertSame('{"name":["value"]}', $this->getSerializer()->serialize($inputs, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); static::assertContainsOnlyInstancesOf(AuthenticationExtension::class, $inputs); } } diff --git a/tests/library/Unit/AuthenticatorSelectionCriteriaTest.php b/tests/library/Unit/AuthenticatorSelectionCriteriaTest.php index f03aae892..4ea84046a 100644 --- a/tests/library/Unit/AuthenticatorSelectionCriteriaTest.php +++ b/tests/library/Unit/AuthenticatorSelectionCriteriaTest.php @@ -5,9 +5,9 @@ namespace Webauthn\Tests\Unit; use PHPUnit\Framework\Attributes\Test; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Webauthn\AuthenticatorSelectionCriteria; use Webauthn\Tests\AbstractTestCase; -use const JSON_THROW_ON_ERROR; /** * @internal @@ -27,7 +27,9 @@ public function anAuthenticatorSelectionCriteriaCanBeCreatedAndValueAccessed(): //When $data = $this->getSerializer() - ->deserialize($expectedJson, AuthenticatorSelectionCriteria::class, 'json'); + ->deserialize($expectedJson, AuthenticatorSelectionCriteria::class, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]); //Then static::assertSame( @@ -40,15 +42,23 @@ public function anAuthenticatorSelectionCriteriaCanBeCreatedAndValueAccessed(): ); 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)); + static::assertJsonStringEqualsJsonString($expectedJson, $this->getSerializer()->serialize($data, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); + static::assertJsonStringEqualsJsonString( + $expectedJson, + $this->getSerializer() + ->serialize($authenticatorSelectionCriteria, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]) + ); } #[Test] public function anAuthenticatorSelectionCriteriaWithResidentKeyCanBeCreatedAndValueAccessed(): void { // Given - $expectedJson = '{"requireResidentKey":true,"userVerification":"required","residentKey":"required","authenticatorAttachment":"platform"}'; + $expectedJson = '{"authenticatorAttachment":"platform","requireResidentKey":true,"userVerification":"required","residentKey":"required"}'; $authenticatorSelectionCriteria = AuthenticatorSelectionCriteria::create( AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_PLATFORM, AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED, @@ -58,7 +68,9 @@ public function anAuthenticatorSelectionCriteriaWithResidentKeyCanBeCreatedAndVa //When $data = $this->getSerializer() - ->deserialize($expectedJson, AuthenticatorSelectionCriteria::class, 'json'); + ->deserialize($expectedJson, AuthenticatorSelectionCriteria::class, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]); //Then static::assertSame( @@ -71,7 +83,11 @@ public function anAuthenticatorSelectionCriteriaWithResidentKeyCanBeCreatedAndVa ); static::assertTrue($data->requireResidentKey); static::assertSame(AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_REQUIRED, $data->residentKey); - static::assertSame($expectedJson, json_encode($data, JSON_THROW_ON_ERROR)); - static::assertSame($expectedJson, json_encode($authenticatorSelectionCriteria, JSON_THROW_ON_ERROR)); + static::assertSame($expectedJson, $this->getSerializer()->serialize($data, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); + static::assertSame($expectedJson, $this->getSerializer()->serialize($authenticatorSelectionCriteria, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); } } diff --git a/tests/library/Unit/EntityTest.php b/tests/library/Unit/EntityTest.php index 4363f96fe..9b5661f68 100644 --- a/tests/library/Unit/EntityTest.php +++ b/tests/library/Unit/EntityTest.php @@ -5,15 +5,15 @@ namespace Webauthn\Tests\Unit; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Webauthn\PublicKeyCredentialRpEntity; use Webauthn\PublicKeyCredentialUserEntity; -use const JSON_THROW_ON_ERROR; +use Webauthn\Tests\AbstractTestCase; /** * @internal */ -final class EntityTest extends TestCase +final class EntityTest extends AbstractTestCase { #[Test] public function anPublicKeyCredentialUserEntityCanBeCreatedAndValueAccessed(): void @@ -25,8 +25,11 @@ public function anPublicKeyCredentialUserEntityCanBeCreatedAndValueAccessed(): v static::assertSame('icon', $user->icon); static::assertSame('id', $user->id); static::assertSame( - '{"name":"name","icon":"icon","id":"aWQ","displayName":"display_name"}', - json_encode($user, JSON_THROW_ON_ERROR) + '{"id":"aWQ","name":"name","displayName":"display_name","icon":"icon"}', + $this->getSerializer() + ->serialize($user, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]) ); } @@ -38,6 +41,8 @@ public function anPublicKeyCredentialRpEntityCanBeCreatedAndValueAccessed(): voi static::assertSame('name', $rp->name); static::assertSame('icon', $rp->icon); static::assertSame('id', $rp->id); - static::assertSame('{"name":"name","icon":"icon","id":"id"}', json_encode($rp, JSON_THROW_ON_ERROR)); + static::assertSame('{"id":"id","name":"name","icon":"icon"}', $this->getSerializer()->serialize($rp, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); } } diff --git a/tests/library/Unit/PublicKeyCredentialCreationOptionsTest.php b/tests/library/Unit/PublicKeyCredentialCreationOptionsTest.php index 0f60119f3..2164f342d 100644 --- a/tests/library/Unit/PublicKeyCredentialCreationOptionsTest.php +++ b/tests/library/Unit/PublicKeyCredentialCreationOptionsTest.php @@ -5,13 +5,13 @@ namespace Webauthn\Tests\Unit; use PHPUnit\Framework\Attributes\Test; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Webauthn\PublicKeyCredentialCreationOptions; use Webauthn\PublicKeyCredentialDescriptor; use Webauthn\PublicKeyCredentialParameters; use Webauthn\PublicKeyCredentialRpEntity; use Webauthn\PublicKeyCredentialUserEntity; use Webauthn\Tests\AbstractTestCase; -use const JSON_THROW_ON_ERROR; /** * @internal @@ -42,9 +42,12 @@ public function anPublicKeyCredentialCreationOptionsCanBeCreatedAndValueAccessed static::assertSame([$credentialParameters], $options->pubKeyCredParams); static::assertSame('direct', $options->attestation); static::assertSame(1000, $options->timeout); - static::assertSame( + static::assertJsonStringEqualsJsonString( '{"rp":{"name":"RP"},"user":{"name":"USER","id":"aWQ","displayName":"FOO BAR"},"challenge":"Y2hhbGxlbmdl","pubKeyCredParams":[{"type":"type","alg":-100}],"timeout":1000,"excludeCredentials":[{"type":"type","id":"aWQ","transports":["transport"]}],"attestation":"direct"}', - json_encode($options, JSON_THROW_ON_ERROR) + $this->getSerializer() + ->serialize($options, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]) ); $data = $this->getSerializer() @@ -56,9 +59,12 @@ public function anPublicKeyCredentialCreationOptionsCanBeCreatedAndValueAccessed static::assertSame('challenge', $data->challenge); static::assertSame('direct', $data->attestation); static::assertSame(1000, $data->timeout); - static::assertSame( + static::assertJsonStringEqualsJsonString( '{"rp":{"name":"RP"},"user":{"name":"USER","id":"aWQ","displayName":"FOO BAR"},"challenge":"Y2hhbGxlbmdl","pubKeyCredParams":[{"type":"type","alg":-100}],"timeout":1000,"excludeCredentials":[{"type":"type","id":"aWQ","transports":["transport"]}],"authenticatorSelection":{"requireResidentKey":false,"userVerification":"preferred","residentKey":"preferred"},"attestation":"direct"}', - json_encode($data, JSON_THROW_ON_ERROR) + $this->getSerializer() + ->serialize($data, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]) ); } @@ -79,15 +85,19 @@ public function anPublicKeyCredentialCreationOptionsWithoutExcludeCredentialsCan timeout: 1000 ); - $json = json_encode($options, JSON_THROW_ON_ERROR); - static::assertSame( - // '{"rp":{"name":"RP"},"pubKeyCredParams":[{"type":"type","alg":-100}],"challenge":"Y2hhbGxlbmdl","attestation":"indirect","user":{"name":"USER","id":"aWQ","displayName":"FOO BAR"},"authenticatorSelection":{"requireResidentKey":false,"userVerification":"preferred","residentKey":"preferred"},"excludeCredentials":[],"timeout":1000}', // TODO: On hold. Waiting for issue clarification. See https://github.com/fido-alliance/conformance-test-tools-resources/issues/676 + $json = $this->getSerializer() + ->serialize($options, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]); + static::assertJsonStringEqualsJsonString( '{"rp":{"name":"RP"},"user":{"name":"USER","id":"aWQ","displayName":"FOO BAR"},"challenge":"Y2hhbGxlbmdl","pubKeyCredParams":[{"type":"type","alg":-100}],"timeout":1000,"attestation":"indirect"}', $json ); $data = $this->getSerializer() - ->deserialize($json, PublicKeyCredentialCreationOptions::class, 'json'); + ->deserialize($json, PublicKeyCredentialCreationOptions::class, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]); static::assertSame([], $data->excludeCredentials); } } diff --git a/tests/library/Unit/PublicKeyCredentialDescriptorTest.php b/tests/library/Unit/PublicKeyCredentialDescriptorTest.php index 77d6c294f..99ab9b7ef 100644 --- a/tests/library/Unit/PublicKeyCredentialDescriptorTest.php +++ b/tests/library/Unit/PublicKeyCredentialDescriptorTest.php @@ -5,14 +5,14 @@ namespace Webauthn\Tests\Unit; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Webauthn\PublicKeyCredentialDescriptor; -use const JSON_THROW_ON_ERROR; +use Webauthn\Tests\AbstractTestCase; /** * @internal */ -final class PublicKeyCredentialDescriptorTest extends TestCase +final class PublicKeyCredentialDescriptorTest extends AbstractTestCase { #[Test] public function anPublicKeyCredentialDescriptorCanBeCreatedAndValueAccessed(): void @@ -24,7 +24,10 @@ public function anPublicKeyCredentialDescriptorCanBeCreatedAndValueAccessed(): v static::assertSame(['transport'], $descriptor->transports); static::assertSame( '{"type":"type","id":"aWQ","transports":["transport"]}', - json_encode($descriptor, JSON_THROW_ON_ERROR) + $this->getSerializer() + ->serialize($descriptor, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]) ); } } diff --git a/tests/library/Unit/PublicKeyCredentialParametersTest.php b/tests/library/Unit/PublicKeyCredentialParametersTest.php index 016322488..7ba1789f7 100644 --- a/tests/library/Unit/PublicKeyCredentialParametersTest.php +++ b/tests/library/Unit/PublicKeyCredentialParametersTest.php @@ -5,9 +5,9 @@ namespace Webauthn\Tests\Unit; use PHPUnit\Framework\Attributes\Test; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Webauthn\PublicKeyCredentialParameters; use Webauthn\Tests\AbstractTestCase; -use const JSON_THROW_ON_ERROR; /** * @internal @@ -21,12 +21,16 @@ public function aPublicKeyCredentialParametersCanBeCreatedAndValueAccessed(): vo static::assertSame('type', $parameters->type); static::assertSame(100, $parameters->alg); - static::assertSame('{"type":"type","alg":100}', json_encode($parameters, JSON_THROW_ON_ERROR)); + static::assertSame('{"type":"type","alg":100}', $this->getSerializer()->serialize($parameters, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); $data = $this->getSerializer() ->deserialize('{"type":"type","alg":100}', PublicKeyCredentialParameters::class, 'json'); static::assertSame('type', $data->type); static::assertSame(100, $data->alg); - static::assertSame('{"type":"type","alg":100}', json_encode($data, JSON_THROW_ON_ERROR)); + static::assertSame('{"type":"type","alg":100}', $this->getSerializer()->serialize($data, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); } } diff --git a/tests/library/Unit/PublicKeyCredentialRequestOptionsTest.php b/tests/library/Unit/PublicKeyCredentialRequestOptionsTest.php index 808a5d0c7..0ed8a85d2 100644 --- a/tests/library/Unit/PublicKeyCredentialRequestOptionsTest.php +++ b/tests/library/Unit/PublicKeyCredentialRequestOptionsTest.php @@ -5,13 +5,13 @@ namespace Webauthn\Tests\Unit; use PHPUnit\Framework\Attributes\Test; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Webauthn\AuthenticationExtensions\AuthenticationExtension; use Webauthn\AuthenticationExtensions\AuthenticationExtensions; use Webauthn\AuthenticationExtensions\AuthenticationExtensionsClientInputs; use Webauthn\PublicKeyCredentialDescriptor; use Webauthn\PublicKeyCredentialRequestOptions; use Webauthn\Tests\AbstractTestCase; -use const JSON_THROW_ON_ERROR; /** * @internal @@ -24,7 +24,10 @@ public function authenticatorExtensionSerialization(): void // Given $extensions = AuthenticationExtensions::create([AuthenticationExtension::create('foo', 'bar')]); $extensions['baz'] = 'New era'; - $json = json_encode($extensions, JSON_THROW_ON_ERROR); + $json = $this->getSerializer() + ->serialize($extensions, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]); // When $data = $this->getSerializer() @@ -35,7 +38,9 @@ public function authenticatorExtensionSerialization(): void static::assertSame('bar', $data->get('foo')->value); static::assertSame('bar', $data['foo']->value); static::assertSame('New era', $data['baz']->value); - static::assertSame($json, json_encode($data, JSON_THROW_ON_ERROR)); + static::assertSame($json, $this->getSerializer()->serialize($data, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); } #[Test] @@ -59,9 +64,12 @@ public function aPublicKeyCredentialRequestOptionsCanBeCreatedAndValueAccessed() static::assertSame([$credential], $publicKeyCredentialRequestOptions->allowCredentials); static::assertSame('preferred', $publicKeyCredentialRequestOptions->userVerification); static::assertInstanceOf(AuthenticationExtensions::class, $publicKeyCredentialRequestOptions->extensions); - static::assertSame( + static::assertJsonStringEqualsJsonString( '{"challenge":"Y2hhbGxlbmdl","rpId":"rp_id","userVerification":"preferred","allowCredentials":[{"type":"type","id":"aWQ","transports":["transport"]}],"extensions":{"foo":"bar"},"timeout":1000}', - json_encode($publicKeyCredentialRequestOptions, JSON_THROW_ON_ERROR) + $this->getSerializer() + ->serialize($publicKeyCredentialRequestOptions, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]) ); $data = $this->getSerializer() @@ -75,9 +83,12 @@ public function aPublicKeyCredentialRequestOptionsCanBeCreatedAndValueAccessed() static::assertSame('rp_id', $data->rpId); static::assertSame('preferred', $data->userVerification); static::assertInstanceOf(AuthenticationExtensions::class, $data->extensions); - static::assertSame( + static::assertJsonStringEqualsJsonString( '{"challenge":"Y2hhbGxlbmdl","rpId":"rp_id","userVerification":"preferred","allowCredentials":[{"type":"type","id":"aWQ","transports":["transport"]}],"extensions":{"foo":"bar"},"timeout":1000}', - json_encode($data, JSON_THROW_ON_ERROR) + $this->getSerializer() + ->serialize($data, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]) ); } } diff --git a/tests/library/Unit/PublicKeyCredentialSourceTest.php b/tests/library/Unit/PublicKeyCredentialSourceTest.php index d8955aca5..cde05124b 100644 --- a/tests/library/Unit/PublicKeyCredentialSourceTest.php +++ b/tests/library/Unit/PublicKeyCredentialSourceTest.php @@ -5,12 +5,11 @@ namespace Webauthn\Tests\Unit; use PHPUnit\Framework\Attributes\Test; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Uid\Uuid; use Webauthn\PublicKeyCredentialSource; use Webauthn\Tests\AbstractTestCase; use Webauthn\TrustPath\EmptyTrustPath; -use const JSON_UNESCAPED_SLASHES; -use const JSON_UNESCAPED_UNICODE; /** * @internal @@ -20,11 +19,45 @@ final class PublicKeyCredentialSourceTest extends AbstractTestCase #[Test] public function backwardCompatibilityIsEnsured(): void { - $data = '{"publicKeyCredentialId":"cHVibGljS2V5Q3JlZGVudGlhbElk","type":"type","transports":["transport1","transport2"],"attestationType":"attestationType","trustPath":{"type":"Webauthn\\\\TrustPath\\\\EmptyTrustPath"},"aaguid":"014c0f17-f86f-4586-9914-2779922ba877","credentialPublicKey":"cHVibGljS2V5","userHandle":"dXNlckhhbmRsZQ","counter":123456789}'; - $source = $this->getSerializer() + // Given + $data = '{"publicKeyCredentialId":"cHVibGljS2V5Q3JlZGVudGlhbElk","type":"type","transports":["transport1","transport2"],"attestationType":"attestationType","trustPath":[],"aaguid":"014c0f17-f86f-4586-9914-2779922ba877","credentialPublicKey":"cHVibGljS2V5","userHandle":"dXNlckhhbmRsZQ","counter":123456789}'; + + //When + $source1 = $this->getSerializer() ->deserialize($data, PublicKeyCredentialSource::class, 'json'); + $source2 = PublicKeyCredentialSource::createFromArray(json_decode($data, true)); + + static::assertSame('publicKeyCredentialId', $source1->publicKeyCredentialId); + static::assertEquals($source1, $source2); + static::assertJsonStringEqualsJsonString($data, $this->getSerializer()->serialize($source2, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); + } + + #[Test] + public function jsonObject(): void + { + $serializer = $this->getSerializer(); + $source = publicKeyCredentialSource::create( + 'publicKeyCredentialId', + 'type', + ['transport1', 'transport2'], + 'attestationType', + EmptyTrustPath::create(), + Uuid::fromString('02ffd35d-7f0c-46b5-9eae-851ee4807b25'), + 'publicKey', + 'userHandle', + 123_456_789 + ); + $asJson = $this->getSerializer() + ->serialize($source, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]); + + $object1 = $serializer->deserialize($asJson, PublicKeyCredentialSource::class, 'json'); + $object2 = PublicKeyCredentialSource::createFromArray(json_decode($asJson, true)); - static::assertSame('publicKeyCredentialId', $source->publicKeyCredentialId); + static::assertEquals($object1, $object2); } #[Test] @@ -46,9 +79,12 @@ public function objectSerialization(): void false ); - static::assertSame( - '{"publicKeyCredentialId":"cHVibGljS2V5Q3JlZGVudGlhbElk","type":"type","transports":["transport1","transport2"],"attestationType":"attestationType","trustPath":{"type":"Webauthn\\\\TrustPath\\\\EmptyTrustPath"},"aaguid":"02ffd35d-7f0c-46b5-9eae-851ee4807b25","credentialPublicKey":"cHVibGljS2V5","userHandle":"dXNlckhhbmRsZQ","counter":123456789,"otherUI":null,"backupEligible":true,"backupStatus":true,"uvInitialized":false}', - json_encode($source, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) + static::assertJsonStringEqualsJsonString( + '{"publicKeyCredentialId":"cHVibGljS2V5Q3JlZGVudGlhbElk","type":"type","transports":["transport1","transport2"],"attestationType":"attestationType","trustPath":[],"aaguid":"02ffd35d-7f0c-46b5-9eae-851ee4807b25","credentialPublicKey":"cHVibGljS2V5","userHandle":"dXNlckhhbmRsZQ","counter":123456789,"backupEligible":true,"backupStatus":true,"uvInitialized":false}', + $this->getSerializer() + ->serialize($source, 'json', [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ]) ); } } diff --git a/tests/library/Unit/TrustPath/TrustPathTest.php b/tests/library/Unit/TrustPath/TrustPathTest.php index 769149945..5a797d063 100644 --- a/tests/library/Unit/TrustPath/TrustPathTest.php +++ b/tests/library/Unit/TrustPath/TrustPathTest.php @@ -46,7 +46,7 @@ public function anEcdaaKeyIdTrustPathCanBeCreated(): void public function theLoaderCannotLoadUnsupportedTypeName(): void { $this->expectException(InvalidTrustPathException::class); - $this->expectExceptionMessage('The trust path type "foo" is not supported'); + $this->expectExceptionMessage('Unsupported trust path'); TrustPathLoader::loadTrustPath([ 'type' => 'foo', ]); @@ -59,9 +59,7 @@ public function theLoaderCannotLoadUnsupportedTypeName(): void public function theLoaderCannotLoadUnsupportedTypeNameBasedOnClass(): void { $this->expectException(InvalidTrustPathException::class); - $this->expectExceptionMessage( - 'The trust path type "Webauthn\Tests\Unit\TrustPath\NotAValidTrustPath" is not supported' - ); + $this->expectExceptionMessage('Unsupported trust path'); TrustPathLoader::loadTrustPath([ 'type' => NotAValidTrustPath::class, ]); diff --git a/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php b/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php index ef91ea582..afa635868 100644 --- a/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php +++ b/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php @@ -54,7 +54,6 @@ public function anExistingUserCanAskForOptionsUsingTheDedicatedController(): voi 'status', 'errorMessage', 'rp', - 'pubKeyCredParams', 'challenge', 'attestation', 'user', diff --git a/tests/symfony/functional/Firewall/RegistrationAreaTest.php b/tests/symfony/functional/Firewall/RegistrationAreaTest.php index 473830a2a..9bd96006e 100644 --- a/tests/symfony/functional/Firewall/RegistrationAreaTest.php +++ b/tests/symfony/functional/Firewall/RegistrationAreaTest.php @@ -103,10 +103,10 @@ public function aValidRequestProcessed(): void static::assertArrayHasKey('authenticatorSelection', $data); static::assertSame([ + 'authenticatorAttachment' => 'cross-platform', 'requireResidentKey' => true, 'userVerification' => 'required', 'residentKey' => 'required', - 'authenticatorAttachment' => 'cross-platform', ], $data['authenticatorSelection']); } @@ -188,10 +188,10 @@ public function aValidRequestProcessedWithExtensions(): void static::assertArrayHasKey('authenticatorSelection', $data); static::assertSame([ + 'authenticatorAttachment' => 'platform', 'requireResidentKey' => true, 'userVerification' => 'required', 'residentKey' => 'required', - 'authenticatorAttachment' => 'platform', ], $data['authenticatorSelection']); } From 26ef755f58f1aece6465afa56fd465915f53d60c Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sun, 7 Jul 2024 08:49:44 +0200 Subject: [PATCH 4/7] Add VerificationMethodANDCombinationsDenormalizer and trigger deprecation messages (#620) Added `VerificationMethodANDCombinationsDenormalizer` and included in serializer configuration. Also, implemented trigger_deprecation in jsonSerialize methods marking them as deprecated and suggesting the use of the serializer instead. Further method and class modifications are reflected in the phpstan-baseline.neon file. --- phpstan-baseline.neon | 50 +++++++++++++++++++ .../MetadataStatementSerializerFactory.php | 1 + ...ationMethodANDCombinationsDenormalizer.php | 45 +++++++++++++++++ .../src/Service/MetadataBLOBPayload.php | 12 +++++ .../src/Service/MetadataBLOBPayloadEntry.php | 6 +++ .../src/Statement/AlternativeDescriptions.php | 6 +++ .../src/Statement/AuthenticatorGetInfo.php | 6 +++ .../Statement/BiometricAccuracyDescriptor.php | 6 +++ .../src/Statement/BiometricStatusReport.php | 6 +++ .../src/Statement/CodeAccuracyDescriptor.php | 6 +++ .../DisplayPNGCharacteristicsDescriptor.php | 6 +++ .../src/Statement/EcdaaTrustAnchor.php | 6 +++ .../src/Statement/ExtensionDescriptor.php | 6 +++ .../src/Statement/MetadataStatement.php | 6 +++ .../Statement/PatternAccuracyDescriptor.php | 6 +++ .../src/Statement/RgbPaletteEntry.php | 6 +++ .../src/Statement/RogueListEntry.php | 6 +++ .../src/Statement/StatusReport.php | 6 +++ .../VerificationMethodANDCombinations.php | 6 +++ .../VerificationMethodDescriptor.php | 6 +++ .../src/Statement/Version.php | 6 +++ src/symfony/src/Resources/config/services.php | 6 +++ .../AttestationStatement.php | 2 +- src/webauthn/src/AttestedCredentialData.php | 2 +- .../AuthenticationExtension.php | 2 +- .../AuthenticationExtensions.php | 2 +- .../src/AuthenticatorSelectionCriteria.php | 2 +- .../PublicKeyCredentialCreationOptions.php | 2 +- .../src/PublicKeyCredentialDescriptor.php | 2 +- .../src/PublicKeyCredentialParameters.php | 2 +- .../src/PublicKeyCredentialRequestOptions.php | 2 +- .../src/PublicKeyCredentialRpEntity.php | 2 +- .../src/PublicKeyCredentialSource.php | 2 +- .../src/PublicKeyCredentialUserEntity.php | 2 +- .../src/TrustPath/CertificateTrustPath.php | 2 +- src/webauthn/src/TrustPath/EmptyTrustPath.php | 2 +- .../BiometricAccuracyDescriptorObjectTest.php | 11 ++-- .../Unit/CodeAccuracyDescriptorObjectTest.php | 10 ++-- ...isplayPNGCharacteristicsDescriptorTest.php | 3 +- tests/MDS/Unit/MdsTestCase.php | 20 ++++++++ .../PatternAccuracyDescriptorObjectTest.php | 10 ++-- ...icationMethodANDCombinationsObjectTest.php | 11 ++-- ...VerificationMethodDescriptorObjectTest.php | 11 ++-- tests/MDS/Unit/VersionObjectTest.php | 11 ++-- 44 files changed, 287 insertions(+), 44 deletions(-) create mode 100644 src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php create mode 100644 tests/MDS/Unit/MdsTestCase.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ac93686ad..fbc9057a4 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -45,6 +45,56 @@ parameters: count: 1 path: src/metadata-service/src/Denormalizer/ExtensionDescriptorDenormalizer.php + - + message: "#^Method Webauthn\\\\MetadataService\\\\Denormalizer\\\\VerificationMethodANDCombinationsDenormalizer\\:\\:normalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Denormalizer\\\\VerificationMethodANDCombinationsDenormalizer\\:\\:normalize\\(\\) never returns ArrayObject so it can be removed from the return type\\.$#" + count: 1 + path: src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Denormalizer\\\\VerificationMethodANDCombinationsDenormalizer\\:\\:normalize\\(\\) never returns bool so it can be removed from the return type\\.$#" + count: 1 + path: src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Denormalizer\\\\VerificationMethodANDCombinationsDenormalizer\\:\\:normalize\\(\\) never returns float so it can be removed from the return type\\.$#" + count: 1 + path: src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Denormalizer\\\\VerificationMethodANDCombinationsDenormalizer\\:\\:normalize\\(\\) never returns int so it can be removed from the return type\\.$#" + count: 1 + path: src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Denormalizer\\\\VerificationMethodANDCombinationsDenormalizer\\:\\:normalize\\(\\) never returns null so it can be removed from the return type\\.$#" + count: 1 + path: src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Denormalizer\\\\VerificationMethodANDCombinationsDenormalizer\\:\\:normalize\\(\\) never returns string so it can be removed from the return type\\.$#" + count: 1 + path: src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Denormalizer\\\\VerificationMethodANDCombinationsDenormalizer\\:\\:normalize\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Denormalizer\\\\VerificationMethodANDCombinationsDenormalizer\\:\\:normalize\\(\\) return type with generic class ArrayObject does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Denormalizer\\\\VerificationMethodANDCombinationsDenormalizer\\:\\:supportsNormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php + - message: "#^Method Webauthn\\\\MetadataService\\\\Psr18HttpClient\\:\\:request\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" count: 1 diff --git a/src/metadata-service/src/Denormalizer/MetadataStatementSerializerFactory.php b/src/metadata-service/src/Denormalizer/MetadataStatementSerializerFactory.php index e751c8bba..c961ec2f7 100644 --- a/src/metadata-service/src/Denormalizer/MetadataStatementSerializerFactory.php +++ b/src/metadata-service/src/Denormalizer/MetadataStatementSerializerFactory.php @@ -31,6 +31,7 @@ public static function create(): ?SerializerInterface } $denormalizers = [ + new VerificationMethodANDCombinationsDenormalizer(), new ExtensionDescriptorDenormalizer(), new UidNormalizer(), new ArrayDenormalizer(), diff --git a/src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php b/src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php new file mode 100644 index 000000000..89e22f43b --- /dev/null +++ b/src/metadata-service/src/Denormalizer/VerificationMethodANDCombinationsDenormalizer.php @@ -0,0 +1,45 @@ + + */ + public function getSupportedTypes(?string $format): array + { + return [ + VerificationMethodANDCombinations::class => true, + ]; + } + + public function normalize( + mixed $object, + ?string $format = null, + array $context = [] + ): array|string|int|float|bool|ArrayObject|null { + assert($object instanceof VerificationMethodANDCombinations); + + return array_map( + fn ($verificationMethod) => $this->normalizer->normalize($verificationMethod, $format, $context), + $object->verificationMethods + ); + } + + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool + { + return $data instanceof VerificationMethodANDCombinations; + } +} diff --git a/src/metadata-service/src/Service/MetadataBLOBPayload.php b/src/metadata-service/src/Service/MetadataBLOBPayload.php index 3ab82458d..7a55947eb 100644 --- a/src/metadata-service/src/Service/MetadataBLOBPayload.php +++ b/src/metadata-service/src/Service/MetadataBLOBPayload.php @@ -116,6 +116,18 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); $data = [ 'legalHeader' => $this->legalHeader, 'nextUpdate' => $this->nextUpdate, diff --git a/src/metadata-service/src/Service/MetadataBLOBPayloadEntry.php b/src/metadata-service/src/Service/MetadataBLOBPayloadEntry.php index f1d9011a7..be8653bd6 100644 --- a/src/metadata-service/src/Service/MetadataBLOBPayloadEntry.php +++ b/src/metadata-service/src/Service/MetadataBLOBPayloadEntry.php @@ -210,6 +210,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); $data = [ 'aaid' => $this->aaid, 'aaguid' => $this->aaguid, diff --git a/src/metadata-service/src/Statement/AlternativeDescriptions.php b/src/metadata-service/src/Statement/AlternativeDescriptions.php index 228811e08..0baf0f2f4 100644 --- a/src/metadata-service/src/Statement/AlternativeDescriptions.php +++ b/src/metadata-service/src/Statement/AlternativeDescriptions.php @@ -50,6 +50,12 @@ public function add(string $locale, string $description): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); return $this->descriptions; } } diff --git a/src/metadata-service/src/Statement/AuthenticatorGetInfo.php b/src/metadata-service/src/Statement/AuthenticatorGetInfo.php index a39f98309..c1360a61b 100644 --- a/src/metadata-service/src/Statement/AuthenticatorGetInfo.php +++ b/src/metadata-service/src/Statement/AuthenticatorGetInfo.php @@ -40,6 +40,12 @@ public function add(string|int $key, mixed $value): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); return $this->info; } } diff --git a/src/metadata-service/src/Statement/BiometricAccuracyDescriptor.php b/src/metadata-service/src/Statement/BiometricAccuracyDescriptor.php index 14c0e34d4..916398c82 100644 --- a/src/metadata-service/src/Statement/BiometricAccuracyDescriptor.php +++ b/src/metadata-service/src/Statement/BiometricAccuracyDescriptor.php @@ -74,6 +74,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); $data = [ 'selfAttestedFRR' => $this->selfAttestedFRR, 'selfAttestedFAR' => $this->selfAttestedFAR, diff --git a/src/metadata-service/src/Statement/BiometricStatusReport.php b/src/metadata-service/src/Statement/BiometricStatusReport.php index a6b0f3a93..ab5472f82 100644 --- a/src/metadata-service/src/Statement/BiometricStatusReport.php +++ b/src/metadata-service/src/Statement/BiometricStatusReport.php @@ -125,6 +125,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); $data = [ 'certLevel' => $this->certLevel, 'modality' => $this->modality, diff --git a/src/metadata-service/src/Statement/CodeAccuracyDescriptor.php b/src/metadata-service/src/Statement/CodeAccuracyDescriptor.php index cb15d7609..62cff9d58 100644 --- a/src/metadata-service/src/Statement/CodeAccuracyDescriptor.php +++ b/src/metadata-service/src/Statement/CodeAccuracyDescriptor.php @@ -77,6 +77,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); $data = [ 'base' => $this->base, 'minLength' => $this->minLength, diff --git a/src/metadata-service/src/Statement/DisplayPNGCharacteristicsDescriptor.php b/src/metadata-service/src/Statement/DisplayPNGCharacteristicsDescriptor.php index 202264702..6ffcbdab7 100644 --- a/src/metadata-service/src/Statement/DisplayPNGCharacteristicsDescriptor.php +++ b/src/metadata-service/src/Statement/DisplayPNGCharacteristicsDescriptor.php @@ -184,6 +184,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); $data = [ 'width' => $this->width, 'height' => $this->height, diff --git a/src/metadata-service/src/Statement/EcdaaTrustAnchor.php b/src/metadata-service/src/Statement/EcdaaTrustAnchor.php index fd426c13a..0fc307a64 100644 --- a/src/metadata-service/src/Statement/EcdaaTrustAnchor.php +++ b/src/metadata-service/src/Statement/EcdaaTrustAnchor.php @@ -87,6 +87,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); $data = [ 'X' => Base64UrlSafe::encodeUnpadded($this->X), 'Y' => Base64UrlSafe::encodeUnpadded($this->Y), diff --git a/src/metadata-service/src/Statement/ExtensionDescriptor.php b/src/metadata-service/src/Statement/ExtensionDescriptor.php index 4b0404367..29333f187 100644 --- a/src/metadata-service/src/Statement/ExtensionDescriptor.php +++ b/src/metadata-service/src/Statement/ExtensionDescriptor.php @@ -94,6 +94,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); $result = [ 'id' => $this->id, 'tag' => $this->tag, diff --git a/src/metadata-service/src/Statement/MetadataStatement.php b/src/metadata-service/src/Statement/MetadataStatement.php index d14f1e41f..dd6ccc5b0 100644 --- a/src/metadata-service/src/Statement/MetadataStatement.php +++ b/src/metadata-service/src/Statement/MetadataStatement.php @@ -658,6 +658,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); $data = [ 'legalHeader' => $this->legalHeader, 'aaid' => $this->aaid, diff --git a/src/metadata-service/src/Statement/PatternAccuracyDescriptor.php b/src/metadata-service/src/Statement/PatternAccuracyDescriptor.php index 9838e1db2..c151ec6c9 100644 --- a/src/metadata-service/src/Statement/PatternAccuracyDescriptor.php +++ b/src/metadata-service/src/Statement/PatternAccuracyDescriptor.php @@ -65,6 +65,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); $data = [ 'minComplexity' => $this->minComplexity, 'maxRetries' => $this->maxRetries, diff --git a/src/metadata-service/src/Statement/RgbPaletteEntry.php b/src/metadata-service/src/Statement/RgbPaletteEntry.php index a2bd1ea32..2ed58cbd5 100644 --- a/src/metadata-service/src/Statement/RgbPaletteEntry.php +++ b/src/metadata-service/src/Statement/RgbPaletteEntry.php @@ -78,6 +78,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); return [ 'r' => $this->r, 'g' => $this->g, diff --git a/src/metadata-service/src/Statement/RogueListEntry.php b/src/metadata-service/src/Statement/RogueListEntry.php index bea96c6ad..6624d318e 100644 --- a/src/metadata-service/src/Statement/RogueListEntry.php +++ b/src/metadata-service/src/Statement/RogueListEntry.php @@ -62,6 +62,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); return [ 'sk' => $this->sk, 'date' => $this->date, diff --git a/src/metadata-service/src/Statement/StatusReport.php b/src/metadata-service/src/Statement/StatusReport.php index 45b78f6ae..2a8b8377d 100644 --- a/src/metadata-service/src/Statement/StatusReport.php +++ b/src/metadata-service/src/Statement/StatusReport.php @@ -183,6 +183,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); $data = [ 'status' => $this->status, 'effectiveDate' => $this->effectiveDate, diff --git a/src/metadata-service/src/Statement/VerificationMethodANDCombinations.php b/src/metadata-service/src/Statement/VerificationMethodANDCombinations.php index 174dcc83a..581d5f596 100644 --- a/src/metadata-service/src/Statement/VerificationMethodANDCombinations.php +++ b/src/metadata-service/src/Statement/VerificationMethodANDCombinations.php @@ -68,6 +68,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); return $this->verificationMethods; } } diff --git a/src/metadata-service/src/Statement/VerificationMethodDescriptor.php b/src/metadata-service/src/Statement/VerificationMethodDescriptor.php index 3fb2aeb92..0f2a228cf 100644 --- a/src/metadata-service/src/Statement/VerificationMethodDescriptor.php +++ b/src/metadata-service/src/Statement/VerificationMethodDescriptor.php @@ -255,6 +255,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); $data = [ 'userVerificationMethod' => $this->userVerificationMethod, 'caDesc' => $this->caDesc, diff --git a/src/metadata-service/src/Statement/Version.php b/src/metadata-service/src/Statement/Version.php index 8588e6201..f01619cd2 100644 --- a/src/metadata-service/src/Statement/Version.php +++ b/src/metadata-service/src/Statement/Version.php @@ -72,6 +72,12 @@ public static function createFromArray(array $data): self */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); $data = [ 'major' => $this->major, 'minor' => $this->minor, diff --git a/src/symfony/src/Resources/config/services.php b/src/symfony/src/Resources/config/services.php index 7f337d611..340625910 100644 --- a/src/symfony/src/Resources/config/services.php +++ b/src/symfony/src/Resources/config/services.php @@ -49,6 +49,7 @@ use Webauthn\FakeCredentialGenerator; use Webauthn\MetadataService\Denormalizer\ExtensionDescriptorDenormalizer; use Webauthn\MetadataService\Denormalizer\MetadataStatementSerializerFactory; +use Webauthn\MetadataService\Denormalizer\VerificationMethodANDCombinationsDenormalizer; use Webauthn\PublicKeyCredentialLoader; use Webauthn\PublicKeyCredentialSourceRepository; use Webauthn\SimpleFakeCredentialGenerator; @@ -196,6 +197,11 @@ $container ->alias('webauthn.request_factory.default', RequestFactoryInterface::class); + $container + ->set(VerificationMethodANDCombinationsDenormalizer::class) + ->tag('serializer.normalizer', [ + 'priority' => 1024, + ]); $container ->set(ExtensionDescriptorDenormalizer::class) ->tag('serializer.normalizer', [ diff --git a/src/webauthn/src/AttestationStatement/AttestationStatement.php b/src/webauthn/src/AttestationStatement/AttestationStatement.php index 4f3004478..5285de2eb 100644 --- a/src/webauthn/src/AttestationStatement/AttestationStatement.php +++ b/src/webauthn/src/AttestationStatement/AttestationStatement.php @@ -177,7 +177,7 @@ public function jsonSerialize(): array trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); return [ diff --git a/src/webauthn/src/AttestedCredentialData.php b/src/webauthn/src/AttestedCredentialData.php index 2f5d74f41..cb226a3f5 100644 --- a/src/webauthn/src/AttestedCredentialData.php +++ b/src/webauthn/src/AttestedCredentialData.php @@ -111,7 +111,7 @@ public function jsonSerialize(): array trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); $result = [ diff --git a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtension.php b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtension.php index 3ed9dc0c9..384476a1e 100644 --- a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtension.php +++ b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtension.php @@ -42,7 +42,7 @@ public function jsonSerialize(): mixed trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); return $this->value; diff --git a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensions.php b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensions.php index 9d7b2bedb..2ef2bc65e 100644 --- a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensions.php +++ b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensions.php @@ -112,7 +112,7 @@ public function jsonSerialize(): array trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); return $this->extensions; diff --git a/src/webauthn/src/AuthenticatorSelectionCriteria.php b/src/webauthn/src/AuthenticatorSelectionCriteria.php index 8e47e92ee..ac794688c 100644 --- a/src/webauthn/src/AuthenticatorSelectionCriteria.php +++ b/src/webauthn/src/AuthenticatorSelectionCriteria.php @@ -232,7 +232,7 @@ public function jsonSerialize(): array trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); $json = [ diff --git a/src/webauthn/src/PublicKeyCredentialCreationOptions.php b/src/webauthn/src/PublicKeyCredentialCreationOptions.php index d6088e97a..3e1377eb5 100644 --- a/src/webauthn/src/PublicKeyCredentialCreationOptions.php +++ b/src/webauthn/src/PublicKeyCredentialCreationOptions.php @@ -316,7 +316,7 @@ public function jsonSerialize(): array trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); $json = [ diff --git a/src/webauthn/src/PublicKeyCredentialDescriptor.php b/src/webauthn/src/PublicKeyCredentialDescriptor.php index eac256903..eedddf500 100644 --- a/src/webauthn/src/PublicKeyCredentialDescriptor.php +++ b/src/webauthn/src/PublicKeyCredentialDescriptor.php @@ -110,7 +110,7 @@ public function jsonSerialize(): array trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); $json = [ diff --git a/src/webauthn/src/PublicKeyCredentialParameters.php b/src/webauthn/src/PublicKeyCredentialParameters.php index 1eab10d57..7ea3cea8f 100644 --- a/src/webauthn/src/PublicKeyCredentialParameters.php +++ b/src/webauthn/src/PublicKeyCredentialParameters.php @@ -86,7 +86,7 @@ public function jsonSerialize(): array trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); return [ diff --git a/src/webauthn/src/PublicKeyCredentialRequestOptions.php b/src/webauthn/src/PublicKeyCredentialRequestOptions.php index a74d97b38..46b6777d5 100644 --- a/src/webauthn/src/PublicKeyCredentialRequestOptions.php +++ b/src/webauthn/src/PublicKeyCredentialRequestOptions.php @@ -207,7 +207,7 @@ public function jsonSerialize(): array trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); $json = [ diff --git a/src/webauthn/src/PublicKeyCredentialRpEntity.php b/src/webauthn/src/PublicKeyCredentialRpEntity.php index bb40b5395..670daefae 100644 --- a/src/webauthn/src/PublicKeyCredentialRpEntity.php +++ b/src/webauthn/src/PublicKeyCredentialRpEntity.php @@ -54,7 +54,7 @@ public function jsonSerialize(): array trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); $json = parent::jsonSerialize(); diff --git a/src/webauthn/src/PublicKeyCredentialSource.php b/src/webauthn/src/PublicKeyCredentialSource.php index ca94d8a5c..2aef84ef0 100644 --- a/src/webauthn/src/PublicKeyCredentialSource.php +++ b/src/webauthn/src/PublicKeyCredentialSource.php @@ -251,7 +251,7 @@ public function jsonSerialize(): array trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); $result = [ diff --git a/src/webauthn/src/PublicKeyCredentialUserEntity.php b/src/webauthn/src/PublicKeyCredentialUserEntity.php index b5c75ee3f..43cdca2eb 100644 --- a/src/webauthn/src/PublicKeyCredentialUserEntity.php +++ b/src/webauthn/src/PublicKeyCredentialUserEntity.php @@ -90,7 +90,7 @@ public function jsonSerialize(): array trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); $json = parent::jsonSerialize(); diff --git a/src/webauthn/src/TrustPath/CertificateTrustPath.php b/src/webauthn/src/TrustPath/CertificateTrustPath.php index 5bc39fbf6..31d250c2e 100644 --- a/src/webauthn/src/TrustPath/CertificateTrustPath.php +++ b/src/webauthn/src/TrustPath/CertificateTrustPath.php @@ -59,7 +59,7 @@ public function jsonSerialize(): array trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); return [ diff --git a/src/webauthn/src/TrustPath/EmptyTrustPath.php b/src/webauthn/src/TrustPath/EmptyTrustPath.php index a91a6121d..e1acb0aba 100644 --- a/src/webauthn/src/TrustPath/EmptyTrustPath.php +++ b/src/webauthn/src/TrustPath/EmptyTrustPath.php @@ -19,7 +19,7 @@ public function jsonSerialize(): array trigger_deprecation( 'web-auth/webauthn-bundle', '4.9.0', - 'The "%s" method is deprecated and will be removed in 5.0. The serializer instead.', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', __METHOD__ ); return [ diff --git a/tests/MDS/Unit/BiometricAccuracyDescriptorObjectTest.php b/tests/MDS/Unit/BiometricAccuracyDescriptorObjectTest.php index dee6dfd34..92a993480 100644 --- a/tests/MDS/Unit/BiometricAccuracyDescriptorObjectTest.php +++ b/tests/MDS/Unit/BiometricAccuracyDescriptorObjectTest.php @@ -6,15 +6,14 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Encoder\JsonEncoder; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Webauthn\MetadataService\Statement\BiometricAccuracyDescriptor; -use const JSON_THROW_ON_ERROR; -use const JSON_UNESCAPED_SLASHES; /** * @internal */ -final class BiometricAccuracyDescriptorObjectTest extends TestCase +final class BiometricAccuracyDescriptorObjectTest extends MdsTestCase { #[Test] #[DataProvider('validObjectData')] @@ -32,7 +31,9 @@ public function validObject( static::assertSame($maxTemplates, $object->maxTemplates); static::assertSame($maxRetries, $object->maxRetries); static::assertSame($blockSlowdown, $object->blockSlowdown); - static::assertSame($expectedJson, json_encode($object, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES)); + static::assertSame($expectedJson, $this->getSerializer()->serialize($object, JsonEncoder::FORMAT, [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); } public static function validObjectData(): iterable diff --git a/tests/MDS/Unit/CodeAccuracyDescriptorObjectTest.php b/tests/MDS/Unit/CodeAccuracyDescriptorObjectTest.php index bfd100a40..344f0832c 100644 --- a/tests/MDS/Unit/CodeAccuracyDescriptorObjectTest.php +++ b/tests/MDS/Unit/CodeAccuracyDescriptorObjectTest.php @@ -6,15 +6,15 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Encoder\JsonEncoder; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; use Webauthn\MetadataService\Statement\CodeAccuracyDescriptor; -use const JSON_UNESCAPED_SLASHES; /** * @internal */ -final class CodeAccuracyDescriptorObjectTest extends TestCase +final class CodeAccuracyDescriptorObjectTest extends MdsTestCase { #[Test] #[DataProvider('validObjectData')] @@ -30,7 +30,9 @@ public function validObject( static::assertSame($minLength, $object->minLength); static::assertSame($maxRetries, $object->maxRetries); static::assertSame($blockSlowdown, $object->blockSlowdown); - static::assertSame($expectedJson, json_encode($object, JSON_UNESCAPED_SLASHES)); + static::assertSame($expectedJson, $this->getSerializer()->serialize($object, JsonEncoder::FORMAT, [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); } public static function validObjectData(): iterable diff --git a/tests/MDS/Unit/DisplayPNGCharacteristicsDescriptorTest.php b/tests/MDS/Unit/DisplayPNGCharacteristicsDescriptorTest.php index f64911f8c..e7a9016f6 100644 --- a/tests/MDS/Unit/DisplayPNGCharacteristicsDescriptorTest.php +++ b/tests/MDS/Unit/DisplayPNGCharacteristicsDescriptorTest.php @@ -6,14 +6,13 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; use Webauthn\MetadataService\Statement\DisplayPNGCharacteristicsDescriptor; /** * @internal */ -final class DisplayPNGCharacteristicsDescriptorTest extends TestCase +final class DisplayPNGCharacteristicsDescriptorTest extends MdsTestCase { #[Test] #[DataProvider('getInvalidValues')] diff --git a/tests/MDS/Unit/MdsTestCase.php b/tests/MDS/Unit/MdsTestCase.php new file mode 100644 index 000000000..681ea97ce --- /dev/null +++ b/tests/MDS/Unit/MdsTestCase.php @@ -0,0 +1,20 @@ +minComplexity); static::assertSame($maxRetries, $object->maxRetries); static::assertSame($blockSlowdown, $object->blockSlowdown); - static::assertSame($expectedJson, json_encode($object, JSON_UNESCAPED_SLASHES)); + static::assertSame($expectedJson, $this->getSerializer()->serialize($object, JsonEncoder::FORMAT, [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); } public static function validObjectData(): iterable diff --git a/tests/MDS/Unit/VerificationMethodANDCombinationsObjectTest.php b/tests/MDS/Unit/VerificationMethodANDCombinationsObjectTest.php index b2f03a02d..84295f4d9 100644 --- a/tests/MDS/Unit/VerificationMethodANDCombinationsObjectTest.php +++ b/tests/MDS/Unit/VerificationMethodANDCombinationsObjectTest.php @@ -6,25 +6,26 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Encoder\JsonEncoder; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Webauthn\MetadataService\Statement\BiometricAccuracyDescriptor; use Webauthn\MetadataService\Statement\CodeAccuracyDescriptor; use Webauthn\MetadataService\Statement\PatternAccuracyDescriptor; use Webauthn\MetadataService\Statement\VerificationMethodANDCombinations; use Webauthn\MetadataService\Statement\VerificationMethodDescriptor; -use const JSON_THROW_ON_ERROR; -use const JSON_UNESCAPED_SLASHES; /** * @internal */ -final class VerificationMethodANDCombinationsObjectTest extends TestCase +final class VerificationMethodANDCombinationsObjectTest extends MdsTestCase { #[Test] #[DataProvider('validObjectData')] public function validObject(VerificationMethodANDCombinations $object, string $expectedJson): void { - static::assertSame($expectedJson, json_encode($object, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES)); + static::assertSame($expectedJson, $this->getSerializer()->serialize($object, JsonEncoder::FORMAT, [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); } public static function validObjectData(): iterable diff --git a/tests/MDS/Unit/VerificationMethodDescriptorObjectTest.php b/tests/MDS/Unit/VerificationMethodDescriptorObjectTest.php index e505671ad..e87862d0a 100644 --- a/tests/MDS/Unit/VerificationMethodDescriptorObjectTest.php +++ b/tests/MDS/Unit/VerificationMethodDescriptorObjectTest.php @@ -6,24 +6,25 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Encoder\JsonEncoder; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Webauthn\MetadataService\Statement\BiometricAccuracyDescriptor; use Webauthn\MetadataService\Statement\CodeAccuracyDescriptor; use Webauthn\MetadataService\Statement\PatternAccuracyDescriptor; use Webauthn\MetadataService\Statement\VerificationMethodDescriptor; -use const JSON_THROW_ON_ERROR; -use const JSON_UNESCAPED_SLASHES; /** * @internal */ -final class VerificationMethodDescriptorObjectTest extends TestCase +final class VerificationMethodDescriptorObjectTest extends MdsTestCase { #[Test] #[DataProvider('validObjectData')] public function validObject(VerificationMethodDescriptor $object, string $expectedJson): void { - static::assertSame($expectedJson, json_encode($object, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES)); + static::assertSame($expectedJson, $this->getSerializer()->serialize($object, JsonEncoder::FORMAT, [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); } public static function validObjectData(): iterable diff --git a/tests/MDS/Unit/VersionObjectTest.php b/tests/MDS/Unit/VersionObjectTest.php index 1de8eab24..7d250b6fe 100644 --- a/tests/MDS/Unit/VersionObjectTest.php +++ b/tests/MDS/Unit/VersionObjectTest.php @@ -6,16 +6,15 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\Encoder\JsonEncoder; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; use Webauthn\MetadataService\Statement\Version; -use const JSON_THROW_ON_ERROR; -use const JSON_UNESCAPED_SLASHES; /** * @internal */ -final class VersionObjectTest extends TestCase +final class VersionObjectTest extends MdsTestCase { #[Test] #[DataProvider('validObjectData')] @@ -23,7 +22,9 @@ public function validObject(Version $object, ?int $major, ?int $minor, string $e { static::assertSame($major, $object->major); static::assertSame($minor, $object->minor); - static::assertSame($expectedJson, json_encode($object, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES)); + static::assertSame($expectedJson, $this->getSerializer()->serialize($object, JsonEncoder::FORMAT, [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + ])); } public static function validObjectData(): iterable From 652815e3b4bb60fbc8b2fdbb8e8bb8a72da09c46 Mon Sep 17 00:00:00 2001 From: Marc Riemer Date: Sun, 7 Jul 2024 21:32:55 +0200 Subject: [PATCH 5/7] Changed ble to internal transport, because internal (Passkeys) are more common. --- src/webauthn/src/SimpleFakeCredentialGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webauthn/src/SimpleFakeCredentialGenerator.php b/src/webauthn/src/SimpleFakeCredentialGenerator.php index a0c4fb90d..9a0f16ac9 100644 --- a/src/webauthn/src/SimpleFakeCredentialGenerator.php +++ b/src/webauthn/src/SimpleFakeCredentialGenerator.php @@ -46,7 +46,7 @@ private function generateCredentials(string $username): array $transports = [ PublicKeyCredentialDescriptor::AUTHENTICATOR_TRANSPORT_USB, PublicKeyCredentialDescriptor::AUTHENTICATOR_TRANSPORT_NFC, - PublicKeyCredentialDescriptor::AUTHENTICATOR_TRANSPORT_BLE, + PublicKeyCredentialDescriptor::AUTHENTICATOR_TRANSPORT_INTERNAL, ]; $credentials = []; for ($i = 0; $i < random_int(1, 3); $i++) { From f546ffad6961bb67c8b013bacfd4e0246f6c351b Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Wed, 10 Jul 2024 17:33:51 +0200 Subject: [PATCH 6/7] Add Credential entity and related tests This update introduces the Credential entity in the Symfony functional tests. Along with the entity, a new test suite for this entity is added, ensuring that the schema for the entity is valid. Additionally, the DATABASE_URL environment variable is introduced in the phpunit configuration to facilitate testing. --- phpunit.xml.dist | 1 + tests/symfony/config/config.yml | 10 -- .../symfony/functional/Entity/Credential.php | 19 ++++ .../symfony/functional/Entity/EntityTest.php | 105 ++++++++++++++++++ 4 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 tests/symfony/functional/Entity/Credential.php create mode 100644 tests/symfony/functional/Entity/EntityTest.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ddc6030b1..ae583fc43 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -22,6 +22,7 @@ + diff --git a/tests/symfony/config/config.yml b/tests/symfony/config/config.yml index d04ba5a3d..75f6c750a 100644 --- a/tests/symfony/config/config.yml +++ b/tests/symfony/config/config.yml @@ -1,6 +1,3 @@ -parameters: - env(DATABASE_URL): '' - framework: test: true secret: 'test' @@ -98,13 +95,6 @@ services: doctrine: dbal: - driver: 'pdo_mysql' - server_version: '5.7' - charset: utf8mb4 - default_table_options: - charset: utf8mb4 - collate: utf8mb4_unicode_ci - url: '%env(resolve:DATABASE_URL)%' orm: enable_lazy_ghost_objects: true diff --git a/tests/symfony/functional/Entity/Credential.php b/tests/symfony/functional/Entity/Credential.php new file mode 100644 index 000000000..595dff21c --- /dev/null +++ b/tests/symfony/functional/Entity/Credential.php @@ -0,0 +1,19 @@ +get(EntityManagerInterface::class); + + //When + $classMetadata = $entityManager->getClassMetadata(Credential::class); + $fields = $classMetadata->fieldMappings; + + //Then + static::assertArrayHasKey($name, $fields); + $field = $fields[$name]; + static::assertSame($type, $field['type']); + static::assertSame($nullable, $field['nullable']); + + } + + public static function expectedFields(): iterable + { + yield [ + 'name' => 'publicKeyCredentialId', + 'type' => 'base64', + 'nullable' => null, + ]; + yield [ + 'name' => 'type', + 'type' => 'string', + 'nullable' => null, + ]; + yield [ + 'name' => 'transports', + 'type' => 'array', + 'nullable' => null, + ]; + yield [ + 'name' => 'attestationType', + 'type' => 'string', + 'nullable' => null, + ]; + yield [ + 'name' => 'trustPath', + 'type' => 'trust_path', + 'nullable' => null, + ]; + yield [ + 'name' => 'aaguid', + 'type' => 'aaguid', + 'nullable' => null, + ]; + yield [ + 'name' => 'credentialPublicKey', + 'type' => 'base64', + 'nullable' => null, + ]; + yield [ + 'name' => 'userHandle', + 'type' => 'string', + 'nullable' => null, + ]; + yield [ + 'name' => 'counter', + 'type' => 'integer', + 'nullable' => null, + ]; + yield [ + 'name' => 'otherUI', + 'type' => 'array', + 'nullable' => true, + ]; + yield [ + 'name' => 'backupEligible', + 'type' => 'boolean', + 'nullable' => true, + ]; + yield [ + 'name' => 'backupStatus', + 'type' => 'boolean', + 'nullable' => true, + ]; + yield [ + 'name' => 'uvInitialized', + 'type' => 'boolean', + 'nullable' => true, + ]; + } +} From 598433f66466bc6dab6b0e0bb452ad02e3bbc92f Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Wed, 10 Jul 2024 17:38:06 +0200 Subject: [PATCH 7/7] Implement serialization/deserialization in Doctrine Types Introduce the `SerializerTrait` to handle serialization and deserialization for Doctrine Types. This refactoring deprecated and replaced the previous ways in these types: `PublicKeyCredentialDescriptorType`, `AttestedCredentialDataType`, `TrustPathDataType`. Meanwhile, test entities were removed, some deprecated methods related to serialization were marked, and the database configuration was updated in the test environment. --- phpstan-baseline.neon | 46 +++++--- phpunit.xml.dist | 1 - .../Type/AttestedCredentialDataType.php | 14 +-- ...ublicKeyCredentialDescriptorCollection.php | 1 + ...cKeyCredentialDescriptorCollectionType.php | 7 +- .../PublicKeyCredentialDescriptorType.php | 13 ++- .../src/Doctrine/Type/SerializerTrait.php | 32 ++++++ .../src/Doctrine/Type/TrustPathDataType.php | 15 ++- src/webauthn/src/AttestedCredentialData.php | 1 + .../WebauthnSerializerFactory.php | 4 + .../src/PublicKeyCredentialDescriptor.php | 4 + ...ublicKeyCredentialDescriptorCollection.php | 6 + .../src/SimpleFakeCredentialGenerator.php | 2 +- .../src/TrustPath/TrustPathLoader.php | 3 + tests/symfony/config/config.yml | 10 ++ .../symfony/functional/Entity/Credential.php | 19 ---- .../symfony/functional/Entity/EntityTest.php | 105 ------------------ 17 files changed, 121 insertions(+), 162 deletions(-) create mode 100644 src/symfony/src/Doctrine/Type/SerializerTrait.php delete mode 100644 tests/symfony/functional/Entity/Credential.php delete mode 100644 tests/symfony/functional/Entity/EntityTest.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index fbc9057a4..1b98fd289 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1176,6 +1176,14 @@ parameters: count: 1 path: src/symfony/src/DependencyInjection/WebauthnExtension.php + - + message: """ + #^Fetching class constant class of deprecated class Webauthn\\\\Bundle\\\\Doctrine\\\\Type\\\\PublicKeyCredentialDescriptorCollectionType\\: + since 4\\.9\\.0 and will be removed in 5\\.0\\.0\\.$# + """ + count: 1 + path: src/symfony/src/DependencyInjection/WebauthnExtension.php + - message: """ #^Fetching class constant class of deprecated interface Webauthn\\\\Bundle\\\\Repository\\\\PublicKeyCredentialUserEntityRepository\\: @@ -1248,12 +1256,17 @@ parameters: path: src/symfony/src/Doctrine/Type/AAGUIDDataType.php - - message: "#^Cannot cast mixed to string\\.$#" + message: "#^Method Webauthn\\\\Bundle\\\\Doctrine\\\\Type\\\\AttestedCredentialDataType\\:\\:convertToDatabaseValue\\(\\) should return string\\|null but returns mixed\\.$#" count: 1 path: src/symfony/src/Doctrine/Type/AttestedCredentialDataType.php - - message: "#^Parameter \\#1 \\$json of static method Webauthn\\\\AttestedCredentialData\\:\\:createFromArray\\(\\) expects array, mixed given\\.$#" + message: "#^Method Webauthn\\\\Bundle\\\\Doctrine\\\\Type\\\\AttestedCredentialDataType\\:\\:convertToPHPValue\\(\\) should return Webauthn\\\\AttestedCredentialData\\|null but returns mixed\\.$#" + count: 1 + path: src/symfony/src/Doctrine/Type/AttestedCredentialDataType.php + + - + message: "#^Parameter \\#1 \\$data of method Webauthn\\\\Bundle\\\\Doctrine\\\\Type\\\\AttestedCredentialDataType\\:\\:deserialize\\(\\) expects string, mixed given\\.$#" count: 1 path: src/symfony/src/Doctrine/Type/AttestedCredentialDataType.php @@ -1298,17 +1311,32 @@ parameters: path: src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorCollection.php - - message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\PublicKeyCredentialDescriptor\\:\\:createFromString\\(\\) expects string, mixed given\\.$#" + message: "#^Method Webauthn\\\\Bundle\\\\Doctrine\\\\Type\\\\PublicKeyCredentialDescriptorType\\:\\:convertToDatabaseValue\\(\\) should return string\\|null but returns mixed\\.$#" count: 1 path: src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorType.php - - message: "#^Cannot cast mixed to string\\.$#" + message: "#^Method Webauthn\\\\Bundle\\\\Doctrine\\\\Type\\\\PublicKeyCredentialDescriptorType\\:\\:convertToPHPValue\\(\\) should return Webauthn\\\\PublicKeyCredentialDescriptor\\|null but returns mixed\\.$#" + count: 1 + path: src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorType.php + + - + message: "#^Parameter \\#1 \\$data of method Webauthn\\\\Bundle\\\\Doctrine\\\\Type\\\\PublicKeyCredentialDescriptorType\\:\\:deserialize\\(\\) expects string, mixed given\\.$#" + count: 1 + path: src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorType.php + + - + message: "#^Method Webauthn\\\\Bundle\\\\Doctrine\\\\Type\\\\TrustPathDataType\\:\\:convertToDatabaseValue\\(\\) should return string\\|null but returns mixed\\.$#" count: 1 path: src/symfony/src/Doctrine/Type/TrustPathDataType.php - - message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\TrustPath\\\\TrustPathLoader\\:\\:loadTrustPath\\(\\) expects array, mixed given\\.$#" + message: "#^Method Webauthn\\\\Bundle\\\\Doctrine\\\\Type\\\\TrustPathDataType\\:\\:convertToPHPValue\\(\\) should return Webauthn\\\\TrustPath\\\\TrustPath\\|null but returns mixed\\.$#" + count: 1 + path: src/symfony/src/Doctrine/Type/TrustPathDataType.php + + - + message: "#^Parameter \\#1 \\$data of method Webauthn\\\\Bundle\\\\Doctrine\\\\Type\\\\TrustPathDataType\\:\\:deserialize\\(\\) expects string, mixed given\\.$#" count: 1 path: src/symfony/src/Doctrine/Type/TrustPathDataType.php @@ -3403,14 +3431,6 @@ parameters: count: 1 path: src/webauthn/src/TrustPath/EcdaaKeyIdTrustPath.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/TrustPath/TrustPathLoader.php - - message: "#^Parameter \\#1 \\$certificates of static method Webauthn\\\\TrustPath\\\\CertificateTrustPath\\:\\:create\\(\\) expects array\\, array given\\.$#" count: 1 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ae583fc43..ddc6030b1 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -22,7 +22,6 @@ - diff --git a/src/symfony/src/Doctrine/Type/AttestedCredentialDataType.php b/src/symfony/src/Doctrine/Type/AttestedCredentialDataType.php index 4130b04f5..6ab7814a6 100644 --- a/src/symfony/src/Doctrine/Type/AttestedCredentialDataType.php +++ b/src/symfony/src/Doctrine/Type/AttestedCredentialDataType.php @@ -7,17 +7,18 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; use Webauthn\AttestedCredentialData; -use const JSON_THROW_ON_ERROR; final class AttestedCredentialDataType extends Type { + use SerializerTrait; + public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string { - if ($value === null) { + if (! $value instanceof AttestedCredentialData) { return $value; } - return json_encode($value, JSON_THROW_ON_ERROR); + return $this->serialize($value); } public function convertToPHPValue($value, AbstractPlatform $platform): ?AttestedCredentialData @@ -25,14 +26,13 @@ public function convertToPHPValue($value, AbstractPlatform $platform): ?Attested if ($value === null || $value instanceof AttestedCredentialData) { return $value; } - $json = json_decode((string) $value, true, flags: JSON_THROW_ON_ERROR); - return AttestedCredentialData::createFromArray($json); + return $this->deserialize($value, AttestedCredentialData::class); } - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - return $platform->getJsonTypeDeclarationSQL($fieldDeclaration); + return $platform->getJsonTypeDeclarationSQL($column); } public function getName(): string diff --git a/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorCollection.php b/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorCollection.php index 70b796e88..74979c5b5 100644 --- a/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorCollection.php +++ b/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorCollection.php @@ -19,6 +19,7 @@ /** * @implements IteratorAggregate * @internal + * @deprecated since 4.9.0 and will be removed in 5.0.0. */ final class PublicKeyCredentialDescriptorCollection implements JsonSerializable, Countable, IteratorAggregate { diff --git a/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorCollectionType.php b/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorCollectionType.php index 726780b00..eb5c6ae5f 100644 --- a/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorCollectionType.php +++ b/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorCollectionType.php @@ -10,6 +10,9 @@ use function is_string; use const JSON_THROW_ON_ERROR; +/** + * @deprecated since 4.9.0 and will be removed in 5.0.0. + */ final class PublicKeyCredentialDescriptorCollectionType extends Type { public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string @@ -37,9 +40,9 @@ public function convertToPHPValue( return PublicKeyCredentialDescriptorCollection::createFromString($value); } - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - return $platform->getJsonTypeDeclarationSQL($fieldDeclaration); + return $platform->getJsonTypeDeclarationSQL($column); } public function getName(): string diff --git a/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorType.php b/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorType.php index 85ef449fa..a49d9c19e 100644 --- a/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorType.php +++ b/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorType.php @@ -7,17 +7,18 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; use Webauthn\PublicKeyCredentialDescriptor; -use const JSON_THROW_ON_ERROR; final class PublicKeyCredentialDescriptorType extends Type { + use SerializerTrait; + public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string { - if ($value === null) { + if (! $value instanceof PublicKeyCredentialDescriptor) { return $value; } - return json_encode($value, JSON_THROW_ON_ERROR); + return $this->serialize($value); } public function convertToPHPValue($value, AbstractPlatform $platform): ?PublicKeyCredentialDescriptor @@ -26,12 +27,12 @@ public function convertToPHPValue($value, AbstractPlatform $platform): ?PublicKe return $value; } - return PublicKeyCredentialDescriptor::createFromString($value); + return $this->deserialize($value, PublicKeyCredentialDescriptor::class); } - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - return $platform->getJsonTypeDeclarationSQL($fieldDeclaration); + return $platform->getJsonTypeDeclarationSQL($column); } public function getName(): string diff --git a/src/symfony/src/Doctrine/Type/SerializerTrait.php b/src/symfony/src/Doctrine/Type/SerializerTrait.php new file mode 100644 index 000000000..b9b652885 --- /dev/null +++ b/src/symfony/src/Doctrine/Type/SerializerTrait.php @@ -0,0 +1,32 @@ +create(); + + return $serializer->serialize($data, JsonEncoder::FORMAT, [ + AbstractObjectNormalizer::SKIP_NULL_VALUES => true, + JsonEncode::OPTIONS => JSON_THROW_ON_ERROR, + ]); + } + + protected function deserialize(string $data, string $class): mixed + { + $serializer = (new WebauthnSerializerFactory(AttestationStatementSupportManager::create()))->create(); + + return $serializer->deserialize($data, $class, JsonEncoder::FORMAT); + } +} diff --git a/src/symfony/src/Doctrine/Type/TrustPathDataType.php b/src/symfony/src/Doctrine/Type/TrustPathDataType.php index 677dfc6ce..76ba2ecc2 100644 --- a/src/symfony/src/Doctrine/Type/TrustPathDataType.php +++ b/src/symfony/src/Doctrine/Type/TrustPathDataType.php @@ -7,18 +7,18 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; use Webauthn\TrustPath\TrustPath; -use Webauthn\TrustPath\TrustPathLoader; -use const JSON_THROW_ON_ERROR; final class TrustPathDataType extends Type { + use SerializerTrait; + public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string { - if ($value === null) { + if (! $value instanceof TrustPath) { return $value; } - return json_encode($value, JSON_THROW_ON_ERROR); + return $this->serialize($value); } public function convertToPHPValue($value, AbstractPlatform $platform): ?TrustPath @@ -26,14 +26,13 @@ public function convertToPHPValue($value, AbstractPlatform $platform): ?TrustPat if ($value === null || $value instanceof TrustPath) { return $value; } - $json = json_decode((string) $value, true, flags: JSON_THROW_ON_ERROR); - return TrustPathLoader::loadTrustPath($json); + return $this->deserialize($value, TrustPath::class); } - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { - return $platform->getJsonTypeDeclarationSQL($fieldDeclaration); + return $platform->getJsonTypeDeclarationSQL($column); } public function getName(): string diff --git a/src/webauthn/src/AttestedCredentialData.php b/src/webauthn/src/AttestedCredentialData.php index cb226a3f5..a97f3fa49 100644 --- a/src/webauthn/src/AttestedCredentialData.php +++ b/src/webauthn/src/AttestedCredentialData.php @@ -66,6 +66,7 @@ public function getCredentialPublicKey(): ?string /** * @param mixed[] $json + * @deprecated since 4.9.0 and will be removed in 5.0.0. Please use the serializer instead. */ public static function createFromArray(array $json): self { diff --git a/src/webauthn/src/Denormalizer/WebauthnSerializerFactory.php b/src/webauthn/src/Denormalizer/WebauthnSerializerFactory.php index 694039a01..258a4bfb4 100644 --- a/src/webauthn/src/Denormalizer/WebauthnSerializerFactory.php +++ b/src/webauthn/src/Denormalizer/WebauthnSerializerFactory.php @@ -15,6 +15,8 @@ use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\SerializerInterface; use Webauthn\AttestationStatement\AttestationStatementSupportManager; +use Webauthn\MetadataService\Denormalizer\ExtensionDescriptorDenormalizer; +use Webauthn\MetadataService\Denormalizer\VerificationMethodANDCombinationsDenormalizer; final class WebauthnSerializerFactory { @@ -42,6 +44,8 @@ public function create(): SerializerInterface } $denormalizers = [ + new ExtensionDescriptorDenormalizer(), + new VerificationMethodANDCombinationsDenormalizer(), new AuthenticationExtensionNormalizer(), new PublicKeyCredentialDescriptorNormalizer(), new AttestedCredentialDataNormalizer(), diff --git a/src/webauthn/src/PublicKeyCredentialDescriptor.php b/src/webauthn/src/PublicKeyCredentialDescriptor.php index eedddf500..3a351be27 100644 --- a/src/webauthn/src/PublicKeyCredentialDescriptor.php +++ b/src/webauthn/src/PublicKeyCredentialDescriptor.php @@ -79,6 +79,9 @@ public function getTransports(): array return $this->transports; } + /** + * @deprecated since 4.9.0 and will be removed in 5.0.0. Please use the serializer instead. + */ public static function createFromString(string $data): self { $data = json_decode($data, true, flags: JSON_THROW_ON_ERROR); @@ -88,6 +91,7 @@ public static function createFromString(string $data): self /** * @param mixed[] $json + * @deprecated since 4.9.0 and will be removed in 5.0.0. Please use the serializer instead. */ public static function createFromArray(array $json): self { diff --git a/src/webauthn/src/PublicKeyCredentialDescriptorCollection.php b/src/webauthn/src/PublicKeyCredentialDescriptorCollection.php index bbc3a8c1b..d5f1481c9 100644 --- a/src/webauthn/src/PublicKeyCredentialDescriptorCollection.php +++ b/src/webauthn/src/PublicKeyCredentialDescriptorCollection.php @@ -100,6 +100,12 @@ public function count(int $mode = COUNT_NORMAL): int */ public function jsonSerialize(): array { + trigger_deprecation( + 'web-auth/webauthn-bundle', + '4.9.0', + 'The "%s" method is deprecated and will be removed in 5.0. Please use the serializer instead.', + __METHOD__ + ); return $this->publicKeyCredentialDescriptors; } diff --git a/src/webauthn/src/SimpleFakeCredentialGenerator.php b/src/webauthn/src/SimpleFakeCredentialGenerator.php index 9a0f16ac9..a0c4fb90d 100644 --- a/src/webauthn/src/SimpleFakeCredentialGenerator.php +++ b/src/webauthn/src/SimpleFakeCredentialGenerator.php @@ -46,7 +46,7 @@ private function generateCredentials(string $username): array $transports = [ PublicKeyCredentialDescriptor::AUTHENTICATOR_TRANSPORT_USB, PublicKeyCredentialDescriptor::AUTHENTICATOR_TRANSPORT_NFC, - PublicKeyCredentialDescriptor::AUTHENTICATOR_TRANSPORT_INTERNAL, + PublicKeyCredentialDescriptor::AUTHENTICATOR_TRANSPORT_BLE, ]; $credentials = []; for ($i = 0; $i < random_int(1, 3); $i++) { diff --git a/src/webauthn/src/TrustPath/TrustPathLoader.php b/src/webauthn/src/TrustPath/TrustPathLoader.php index b79675bc0..017f55252 100644 --- a/src/webauthn/src/TrustPath/TrustPathLoader.php +++ b/src/webauthn/src/TrustPath/TrustPathLoader.php @@ -9,6 +9,9 @@ use function is_array; use function is_string; +/** + * @deprecated since 4.9.0 and will be removed in 5.0.0. Use the serializer instead + */ final class TrustPathLoader { /** diff --git a/tests/symfony/config/config.yml b/tests/symfony/config/config.yml index 75f6c750a..d04ba5a3d 100644 --- a/tests/symfony/config/config.yml +++ b/tests/symfony/config/config.yml @@ -1,3 +1,6 @@ +parameters: + env(DATABASE_URL): '' + framework: test: true secret: 'test' @@ -95,6 +98,13 @@ services: doctrine: dbal: + driver: 'pdo_mysql' + server_version: '5.7' + charset: utf8mb4 + default_table_options: + charset: utf8mb4 + collate: utf8mb4_unicode_ci + url: '%env(resolve:DATABASE_URL)%' orm: enable_lazy_ghost_objects: true diff --git a/tests/symfony/functional/Entity/Credential.php b/tests/symfony/functional/Entity/Credential.php deleted file mode 100644 index 595dff21c..000000000 --- a/tests/symfony/functional/Entity/Credential.php +++ /dev/null @@ -1,19 +0,0 @@ -get(EntityManagerInterface::class); - - //When - $classMetadata = $entityManager->getClassMetadata(Credential::class); - $fields = $classMetadata->fieldMappings; - - //Then - static::assertArrayHasKey($name, $fields); - $field = $fields[$name]; - static::assertSame($type, $field['type']); - static::assertSame($nullable, $field['nullable']); - - } - - public static function expectedFields(): iterable - { - yield [ - 'name' => 'publicKeyCredentialId', - 'type' => 'base64', - 'nullable' => null, - ]; - yield [ - 'name' => 'type', - 'type' => 'string', - 'nullable' => null, - ]; - yield [ - 'name' => 'transports', - 'type' => 'array', - 'nullable' => null, - ]; - yield [ - 'name' => 'attestationType', - 'type' => 'string', - 'nullable' => null, - ]; - yield [ - 'name' => 'trustPath', - 'type' => 'trust_path', - 'nullable' => null, - ]; - yield [ - 'name' => 'aaguid', - 'type' => 'aaguid', - 'nullable' => null, - ]; - yield [ - 'name' => 'credentialPublicKey', - 'type' => 'base64', - 'nullable' => null, - ]; - yield [ - 'name' => 'userHandle', - 'type' => 'string', - 'nullable' => null, - ]; - yield [ - 'name' => 'counter', - 'type' => 'integer', - 'nullable' => null, - ]; - yield [ - 'name' => 'otherUI', - 'type' => 'array', - 'nullable' => true, - ]; - yield [ - 'name' => 'backupEligible', - 'type' => 'boolean', - 'nullable' => true, - ]; - yield [ - 'name' => 'backupStatus', - 'type' => 'boolean', - 'nullable' => true, - ]; - yield [ - 'name' => 'uvInitialized', - 'type' => 'boolean', - 'nullable' => true, - ]; - } -}