From 4bf45ec9e37588b4be8871d20e5005fc7ff5b1c8 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sun, 17 Sep 2023 09:40:02 +0200 Subject: [PATCH] Denormalizers --- composer.json | 1 + phpstan-baseline.neon | 500 ++++++++++++++++-- src/metadata-service/composer.json | 2 + .../CertificateChain/CertificateToolbox.php | 1 + .../ExtensionDescriptorDenormalizer.php | 54 ++ .../MetadataStatementSerializerFactory.php | 58 ++ .../DistantResourceMetadataService.php | 16 +- .../FidoAllianceCompliantMetadataService.php | 24 +- .../Service/FolderResourceMetadataService.php | 23 +- .../src/Service/JsonMetadataService.php | 78 +++ .../Service/LocalResourceMetadataService.php | 25 +- .../src/Service/MetadataBLOBPayload.php | 17 +- .../src/Service/MetadataBLOBPayloadEntry.php | 77 +-- .../src/Service/StringMetadataService.php | 4 + .../src/Statement/AbstractDescriptor.php | 2 + .../src/Statement/AlternativeDescriptions.php | 2 + .../src/Statement/AuthenticatorGetInfo.php | 10 +- .../src/Statement/AuthenticatorStatus.php | 1 + .../Statement/BiometricAccuracyDescriptor.php | 7 +- .../src/Statement/BiometricStatusReport.php | 9 + .../src/Statement/CodeAccuracyDescriptor.php | 10 +- .../DisplayPNGCharacteristicsDescriptor.php | 26 +- .../src/Statement/EcdaaTrustAnchor.php | 10 +- .../src/Statement/ExtensionDescriptor.php | 14 +- .../src/Statement/MetadataStatement.php | 240 +++++---- .../Statement/PatternAccuracyDescriptor.php | 11 +- .../src/Statement/RgbPaletteEntry.php | 5 + .../src/Statement/RogueListEntry.php | 4 + .../src/Statement/StatusReport.php | 18 +- .../VerificationMethodANDCombinations.php | 7 +- .../VerificationMethodDescriptor.php | 14 +- .../src/Statement/Version.php | 12 +- .../src/{Utils.php => ValueFilter.php} | 4 +- .../Controller/AssertionControllerFactory.php | 2 + .../AttestationControllerFactory.php | 2 + .../ProfileBasedCreationOptionsBuilder.php | 32 +- .../ProfileBasedRequestOptionsBuilder.php | 24 +- .../Factory/Security/WebauthnFactory.php | 7 + .../DependencyInjection/WebauthnExtension.php | 8 +- ...ublicKeyCredentialDescriptorCollection.php | 3 + ...licKeyCredentialCreationOptionsRequest.php | 1 + ...licKeyCredentialCreationOptionsRequest.php | 1 + .../src/Repository/CanRegisterUserEntity.php | 1 + .../PublicKeyCredentialSourceRepository.php | 1 + ...ublicKeyCredentialUserEntityRepository.php | 1 + .../src/Resources/config/controller.php | 35 -- src/symfony/src/Resources/config/services.php | 50 +- .../Authentication/Token/WebauthnToken.php | 51 +- .../Token/WebauthnTokenInterface.php | 1 + .../AttestationObject.php | 6 + .../AttestationStatement.php | 8 + .../AttestationStatementSupportManager.php | 5 +- src/webauthn/src/AttestedCredentialData.php | 4 + .../AuthenticationExtension.php | 2 + .../AuthenticationExtensionsClientInputs.php | 6 + .../AuthenticationExtensionsClientOutputs.php | 8 + .../ExtensionOutputError.php | 1 + .../src/AuthenticatorAssertionResponse.php | 3 + .../src/AuthenticatorAttestationResponse.php | 2 + src/webauthn/src/AuthenticatorData.php | 5 + src/webauthn/src/AuthenticatorResponse.php | 1 + .../src/AuthenticatorSelectionCriteria.php | 15 + .../CertificateChainChecker.php | 1 + .../PhpCertificateChainChecker.php | 1 + src/webauthn/src/CertificateToolbox.php | 1 + src/webauthn/src/CollectedClientData.php | 7 + src/webauthn/src/Credential.php | 2 + .../AttestationObjectDenormalizer.php | 70 +++ .../AttestationStatementDenormalizer.php | 39 ++ ...tionExtensionsClientInputsDenormalizer.php | 48 ++ ...enticatorAssertionResponseDenormalizer.php | 57 ++ ...ticatorAttestationResponseDenormalizer.php | 53 ++ .../AuthenticatorDataDenormalizer.php | 147 +++++ .../AuthenticatorResponseDenormalizer.php | 69 +++ .../CollectedClientDataDenormalizer.php | 52 ++ .../PublicKeyCredentialDenormalizer.php | 59 +++ ...PublicKeyCredentialOptionsDenormalizer.php | 68 +++ .../PublicKeyCredentialSourceDenormalizer.php | 56 ++ ...licKeyCredentialUserEntityDenormalizer.php | 53 ++ .../WebauthnSerializerFactory.php | 80 +++ ...AssertionResponseValidationFailedEvent.php | 2 + ...ertionResponseValidationSucceededEvent.php | 1 + ...testationResponseValidationFailedEvent.php | 1 + ...tationResponseValidationSucceededEvent.php | 1 + src/webauthn/src/PublicKeyCredential.php | 3 + .../PublicKeyCredentialCreationOptions.php | 20 + .../src/PublicKeyCredentialDescriptor.php | 3 + ...ublicKeyCredentialDescriptorCollection.php | 10 + .../src/PublicKeyCredentialEntity.php | 2 + .../src/PublicKeyCredentialLoader.php | 22 +- .../src/PublicKeyCredentialOptions.php | 13 + .../src/PublicKeyCredentialParameters.php | 8 + .../src/PublicKeyCredentialRequestOptions.php | 13 + .../src/PublicKeyCredentialRpEntity.php | 3 + .../src/PublicKeyCredentialSource.php | 14 + .../PublicKeyCredentialSourceRepository.php | 1 + .../src/PublicKeyCredentialUserEntity.php | 8 + .../IgnoreTokenBindingHandler.php | 1 + .../TokenBinding/SecTokenBindingHandler.php | 1 + .../src/TokenBinding/TokenBinding.php | 1 + .../src/TokenBinding/TokenBindingHandler.php | 1 + .../TokenBindingNotSupportedHandler.php | 1 + .../src/TrustPath/CertificateTrustPath.php | 5 + .../src/TrustPath/EcdaaKeyIdTrustPath.php | 1 + src/webauthn/src/TrustPath/EmptyTrustPath.php | 4 + src/webauthn/src/TrustPath/TrustPath.php | 2 + tests/library/Functional/AbstractTestCase.php | 37 +- tests/library/Unit/MetadataServiceTest.php | 4 + .../FolderResourceMetadataServiceTest.php | 8 +- tests/symfony/config/config.yml | 1 + .../functional/Assertion/AssertionTest.php | 11 +- .../AdditionalAuthenticatorTest.php | 10 +- .../Attestation/AttestationTest.php | 99 +++- .../PackedAttestationStatementTest.php | 11 +- .../MetadataService/ConformanceTest.php | 40 +- .../symfony/functional/SingleFileService.php | 8 +- 116 files changed, 2370 insertions(+), 436 deletions(-) create mode 100644 src/metadata-service/src/Denormalizer/ExtensionDescriptorDenormalizer.php create mode 100644 src/metadata-service/src/Denormalizer/MetadataStatementSerializerFactory.php create mode 100644 src/metadata-service/src/Service/JsonMetadataService.php rename src/metadata-service/src/{Utils.php => ValueFilter.php} (75%) delete mode 100644 src/symfony/src/Resources/config/controller.php create mode 100644 src/webauthn/src/Denormalizer/AttestationObjectDenormalizer.php create mode 100644 src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php create mode 100644 src/webauthn/src/Denormalizer/AuthenticationExtensionsClientInputsDenormalizer.php create mode 100644 src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php create mode 100644 src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php create mode 100644 src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php create mode 100644 src/webauthn/src/Denormalizer/AuthenticatorResponseDenormalizer.php create mode 100644 src/webauthn/src/Denormalizer/CollectedClientDataDenormalizer.php create mode 100644 src/webauthn/src/Denormalizer/PublicKeyCredentialDenormalizer.php create mode 100644 src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php create mode 100644 src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php create mode 100644 src/webauthn/src/Denormalizer/PublicKeyCredentialUserEntityDenormalizer.php create mode 100644 src/webauthn/src/Denormalizer/WebauthnSerializerFactory.php diff --git a/composer.json b/composer.json index f6ba05b3..7b101dc8 100644 --- a/composer.json +++ b/composer.json @@ -105,6 +105,7 @@ "php-http/curl-client": "^2.2", "php-http/mock-client": "^1.5", "php-parallel-lint/php-parallel-lint": "^1.3", + "phpdocumentor/reflection-docblock": "^5.3", "phpstan/extension-installer": "^1.3", "phpstan/phpstan": "^1.8", "phpstan/phpstan-deprecation-rules": "^1.0", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 229c2715..61f5bc51 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -35,6 +35,21 @@ parameters: count: 2 path: src/metadata-service/src/CertificateChain/PhpCertificateChainValidator.php + - + message: "#^Method Webauthn\\\\MetadataService\\\\Denormalizer\\\\ExtensionDescriptorDenormalizer\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Denormalizer/ExtensionDescriptorDenormalizer.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Denormalizer\\\\ExtensionDescriptorDenormalizer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Denormalizer/ExtensionDescriptorDenormalizer.php + + - + message: "#^Parameter \\#2 \\$array of function array_key_exists expects array, mixed given\\.$#" + count: 1 + path: src/metadata-service/src/Denormalizer/ExtensionDescriptorDenormalizer.php + - message: "#^Call to an undefined method Psr\\\\Http\\\\Client\\\\ClientInterface\\|Symfony\\\\Contracts\\\\HttpClient\\\\HttpClientInterface\\:\\:request\\(\\)\\.$#" count: 1 @@ -45,6 +60,14 @@ parameters: count: 1 path: src/metadata-service/src/Service/DistantResourceMetadataService.php + - + message: """ + #^Call to deprecated method createFromString\\(\\) of class Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\: + since 4\\.7\\.0\\. Please use the symfony/serializer for converting the object\\.$# + """ + count: 1 + path: src/metadata-service/src/Service/DistantResourceMetadataService.php + - message: "#^Cannot call method createRequest\\(\\) on Psr\\\\Http\\\\Message\\\\RequestFactoryInterface\\|null\\.$#" count: 1 @@ -70,6 +93,14 @@ parameters: count: 1 path: src/metadata-service/src/Service/FidoAllianceCompliantMetadataService.php + - + message: """ + #^Call to deprecated method createFromArray\\(\\) of class Webauthn\\\\MetadataService\\\\Service\\\\MetadataBLOBPayloadEntry\\: + since 4\\.7\\.0\\. Please use the symfony/serializer for converting the object\\.$# + """ + count: 1 + path: src/metadata-service/src/Service/FidoAllianceCompliantMetadataService.php + - message: "#^Cannot access offset 'entries' on mixed\\.$#" count: 1 @@ -95,11 +126,35 @@ parameters: count: 1 path: src/metadata-service/src/Service/FidoAllianceCompliantMetadataService.php + - + message: """ + #^Call to deprecated method createFromString\\(\\) of class Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\: + since 4\\.7\\.0\\. Please use the symfony/serializer for converting the object\\.$# + """ + count: 1 + path: src/metadata-service/src/Service/FolderResourceMetadataService.php + - message: "#^Parameter \\#1 \\$string of function trim expects string, string\\|false given\\.$#" count: 1 path: src/metadata-service/src/Service/FolderResourceMetadataService.php + - + message: """ + #^Call to deprecated method createFromString\\(\\) of class Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\: + since 4\\.7\\.0\\. Please use the symfony/serializer for converting the object\\.$# + """ + count: 1 + path: src/metadata-service/src/Service/JsonMetadataService.php + + - + message: """ + #^Call to deprecated method createFromString\\(\\) of class Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\: + since 4\\.7\\.0\\. Please use the symfony/serializer for converting the object\\.$# + """ + count: 1 + path: src/metadata-service/src/Service/LocalResourceMetadataService.php + - message: "#^Parameter \\#1 \\$encodedString of static method ParagonIE\\\\ConstantTime\\\\Base64\\:\\:decode\\(\\) expects string, string\\|false given\\.$#" count: 1 @@ -116,10 +171,15 @@ parameters: path: src/metadata-service/src/Service/MetadataBLOBPayload.php - - message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\MetadataService\\\\Statement\\\\StatusReport\\:\\:createFromArray\\(\\) expects array\\, mixed given\\.$#" + message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(mixed\\)\\: mixed\\)\\|null, Closure\\(array\\)\\: Webauthn\\\\MetadataService\\\\Statement\\\\StatusReport given\\.$#" count: 1 path: src/metadata-service/src/Service/MetadataBLOBPayloadEntry.php + - + message: "#^@readonly property Webauthn\\\\MetadataService\\\\Statement\\\\AuthenticatorGetInfo\\:\\:\\$info is assigned outside of the constructor\\.$#" + count: 1 + path: src/metadata-service/src/Statement/AuthenticatorGetInfo.php + - message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\AuthenticatorGetInfo\\:\\:jsonSerialize\\(\\) should return array\\ but returns array\\\\.$#" count: 1 @@ -206,17 +266,7 @@ parameters: path: src/metadata-service/src/Statement/CodeAccuracyDescriptor.php - - message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\DisplayPNGCharacteristicsDescriptor\\:\\:__construct\\(\\) has parameter \\$plte with no value type specified in iterable type array\\.$#" - count: 1 - path: src/metadata-service/src/Statement/DisplayPNGCharacteristicsDescriptor.php - - - - message: "#^PHPDoc tag @param for parameter \\$width with type array\\ is incompatible with native type int\\.$#" - count: 1 - path: src/metadata-service/src/Statement/DisplayPNGCharacteristicsDescriptor.php - - - - message: "#^PHPDoc type for property Webauthn\\\\MetadataService\\\\Statement\\\\DisplayPNGCharacteristicsDescriptor\\:\\:\\$width with type array\\ is incompatible with native type int\\.$#" + message: "#^@readonly property Webauthn\\\\MetadataService\\\\Statement\\\\DisplayPNGCharacteristicsDescriptor\\:\\:\\$plte is assigned outside of the constructor\\.$#" count: 1 path: src/metadata-service/src/Statement/DisplayPNGCharacteristicsDescriptor.php @@ -280,6 +330,26 @@ parameters: count: 1 path: src/metadata-service/src/Statement/EcdaaTrustAnchor.php + - + message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:__construct\\(\\) has parameter \\$tcDisplayPNGCharacteristics with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Statement/MetadataStatement.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) has parameter \\$alternativeDescriptions with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Statement/MetadataStatement.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) has parameter \\$attachmentHint with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Statement/MetadataStatement.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) has parameter \\$attestationCertificateKeyIdentifiers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Statement/MetadataStatement.php + - message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) has parameter \\$attestationRootCertificates with no value type specified in iterable type array\\.$#" count: 1 @@ -295,6 +365,16 @@ parameters: count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php + - + message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) has parameter \\$ecdaaTrustAnchors with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Statement/MetadataStatement.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) has parameter \\$keyProtection with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Statement/MetadataStatement.php + - message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) has parameter \\$matcherProtection with no value type specified in iterable type array\\.$#" count: 1 @@ -305,11 +385,21 @@ parameters: count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php + - + message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) has parameter \\$supportedExtensions with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Statement/MetadataStatement.php + - message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) has parameter \\$tcDisplay with no value type specified in iterable type array\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php + - + message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) has parameter \\$tcDisplayPNGCharacteristics with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Statement/MetadataStatement.php + - message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) has parameter \\$upv with no value type specified in iterable type array\\.$#" count: 1 @@ -321,22 +411,22 @@ parameters: path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\MetadataService\\\\CertificateChain\\\\CertificateToolbox\\:\\:fixPEMStructures\\(\\) expects array\\, mixed given\\.$#" + message: "#^Method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:getAlternativeDescriptions\\(\\) should return Webauthn\\\\MetadataService\\\\Statement\\\\AlternativeDescriptions but returns Webauthn\\\\MetadataService\\\\Statement\\\\AlternativeDescriptions\\|null\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\MetadataService\\\\Statement\\\\AuthenticatorGetInfo\\:\\:create\\(\\) expects array\\, mixed given\\.$#" + message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(mixed\\)\\: mixed\\)\\|null, Closure\\(array\\)\\: Webauthn\\\\MetadataService\\\\Statement\\\\DisplayPNGCharacteristicsDescriptor given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\MetadataService\\\\Statement\\\\DisplayPNGCharacteristicsDescriptor\\:\\:createFromArray\\(\\) expects array\\, array given\\.$#" + message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\MetadataService\\\\CertificateChain\\\\CertificateToolbox\\:\\:fixPEMStructures\\(\\) expects array\\, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\MetadataService\\\\Statement\\\\ExtensionDescriptor\\:\\:createFromArray\\(\\) expects array\\, array given\\.$#" + message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\MetadataService\\\\Statement\\\\ExtensionDescriptor\\:\\:createFromArray\\(\\) expects array\\, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php @@ -361,7 +451,7 @@ parameters: path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Parameter \\#1 \\$descriptions of static method Webauthn\\\\MetadataService\\\\Statement\\\\AlternativeDescriptions\\:\\:create\\(\\) expects array\\, mixed given\\.$#" + message: "#^Parameter \\#1 \\$info of static method Webauthn\\\\MetadataService\\\\Statement\\\\AuthenticatorGetInfo\\:\\:create\\(\\) expects array\\, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php @@ -376,97 +466,110 @@ parameters: path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Parameter \\#2 \\$array of function array_map expects array, mixed given\\.$#" - count: 2 + message: "#^Parameter \\#13 \\$alternativeDescriptions of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects array, mixed given\\.$#" + count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Parameter \\#2 \\$authenticatorVersion of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects int, mixed given\\.$#" + message: "#^Parameter \\#14 \\$legalHeader of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects string\\|null, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Parameter \\#3 \\$protocolFamily of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects string, mixed given\\.$#" + message: "#^Parameter \\#15 \\$aaid of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects string\\|null, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Parameter \\#4 \\$schema of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects int, mixed given\\.$#" + message: "#^Parameter \\#16 \\$aaguid of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects string\\|null, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Parameter \\#6 \\$authenticationAlgorithms of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects array, mixed given\\.$#" + message: "#^Parameter \\#17 \\$attestationCertificateKeyIdentifiers of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects array, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Parameter \\#7 \\$publicKeyAlgAndEncodings of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects array, mixed given\\.$#" + message: "#^Parameter \\#18 \\$keyProtection of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects array, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Parameter \\#8 \\$attestationTypes of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects array, mixed given\\.$#" + message: "#^Parameter \\#19 \\$isKeyRestricted of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects bool\\|null, mixed given\\.$#" + count: 1 + path: src/metadata-service/src/Statement/MetadataStatement.php + + - + message: "#^Parameter \\#2 \\$array of function array_map expects array, mixed given\\.$#" + count: 4 + path: src/metadata-service/src/Statement/MetadataStatement.php + + - + message: "#^Parameter \\#2 \\$authenticatorVersion of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects int, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Property Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:\\$aaguid \\(string\\|null\\) does not accept mixed\\.$#" + message: "#^Parameter \\#20 \\$isFreshUserVerificationRequired of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects bool\\|null, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Property Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:\\$aaid \\(string\\|null\\) does not accept mixed\\.$#" + message: "#^Parameter \\#21 \\$cryptoStrength of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects int\\|null, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Property Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:\\$attachmentHint \\(array\\\\) does not accept mixed\\.$#" + message: "#^Parameter \\#22 \\$attachmentHint of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects array, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Property Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:\\$attestationCertificateKeyIdentifiers \\(array\\\\) does not accept mixed\\.$#" + message: "#^Parameter \\#23 \\$tcDisplayContentType of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects string\\|null, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Property Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:\\$cryptoStrength \\(int\\|null\\) does not accept mixed\\.$#" + message: "#^Parameter \\#25 \\$ecdaaTrustAnchors of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects array, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Property Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:\\$ecdaaTrustAnchors \\(array\\\\) does not accept mixed\\.$#" + message: "#^Parameter \\#26 \\$icon of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects string\\|null, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Property Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:\\$icon \\(string\\|null\\) does not accept mixed\\.$#" + message: "#^Parameter \\#3 \\$protocolFamily of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects string, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Property Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:\\$isFreshUserVerificationRequired \\(bool\\|null\\) does not accept mixed\\.$#" + message: "#^Parameter \\#4 \\$schema of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects int, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Property Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:\\$isKeyRestricted \\(bool\\|null\\) does not accept mixed\\.$#" + message: "#^Parameter \\#6 \\$authenticationAlgorithms of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects array, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Property Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:\\$keyProtection \\(array\\\\) does not accept mixed\\.$#" + message: "#^Parameter \\#7 \\$publicKeyAlgAndEncodings of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects array, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Property Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:\\$legalHeader \\(string\\|null\\) does not accept mixed\\.$#" + message: "#^Parameter \\#8 \\$attestationTypes of static method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:create\\(\\) expects array, mixed given\\.$#" count: 1 path: src/metadata-service/src/Statement/MetadataStatement.php - - message: "#^Property Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:\\$tcDisplayContentType \\(string\\|null\\) does not accept mixed\\.$#" + message: """ + #^Parameter \\$ecdaaTrustAnchors of method Webauthn\\\\MetadataService\\\\Statement\\\\MetadataStatement\\:\\:__construct\\(\\) has typehint with deprecated class Webauthn\\\\MetadataService\\\\Statement\\\\EcdaaTrustAnchor\\: + 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/metadata-service/src/Statement/MetadataStatement.php @@ -495,6 +598,11 @@ parameters: count: 1 path: src/metadata-service/src/Statement/StatusReport.php + - + message: "#^@readonly property Webauthn\\\\MetadataService\\\\Statement\\\\VerificationMethodANDCombinations\\:\\:\\$verificationMethods is assigned outside of the constructor\\.$#" + count: 1 + path: src/metadata-service/src/Statement/VerificationMethodANDCombinations.php + - message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(mixed\\)\\: mixed\\)\\|null, Closure\\(array\\)\\: Webauthn\\\\MetadataService\\\\Statement\\\\VerificationMethodDescriptor given\\.$#" count: 1 @@ -589,6 +697,14 @@ parameters: count: 1 path: src/symfony/src/CredentialOptionsBuilder/ProfileBasedCreationOptionsBuilder.php + - + message: """ + #^Call to deprecated method createFromArray\\(\\) of class Webauthn\\\\AuthenticatorSelectionCriteria\\: + since 4\\.8\\.0\\. Please use \\{Webauthn\\\\Denormalizer\\\\WebauthnSerializerFactory\\} for converting the object\\.$# + """ + count: 1 + path: src/symfony/src/CredentialOptionsBuilder/ProfileBasedCreationOptionsBuilder.php + - message: """ #^Call to deprecated method getContentType\\(\\) of class Symfony\\\\Component\\\\HttpFoundation\\\\Request\\: @@ -1006,11 +1122,6 @@ parameters: count: 1 path: src/symfony/src/Repository/DoctrineCredentialSourceRepository.php - - - message: "#^Class Webauthn\\\\Bundle\\\\Repository\\\\PublicKeyCredentialSourceRepository extends generic class Webauthn\\\\Bundle\\\\Repository\\\\DoctrineCredentialSourceRepository but does not specify its types\\: T$#" - count: 1 - path: src/symfony/src/Repository/PublicKeyCredentialSourceRepository.php - - message: "#^Fetching class constant class of deprecated class Webauthn\\\\PublicKeyCredentialSourceRepository\\.$#" count: 2 @@ -1062,52 +1173,47 @@ parameters: path: src/symfony/src/Security/Authentication/Token/WebauthnToken.php - - message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\AuthenticationExtensions\\\\AuthenticationExtensionsClientOutputs\\:\\:createFromString\\(\\) expects string, mixed given\\.$#" - count: 1 - path: src/symfony/src/Security/Authentication/Token/WebauthnToken.php - - - - message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\PublicKeyCredentialDescriptor\\:\\:createFromString\\(\\) expects string, mixed given\\.$#" + message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$extensions \\(Webauthn\\\\AuthenticationExtensions\\\\AuthenticationExtensionsClientOutputs\\|null\\) does not accept mixed\\.$#" count: 1 path: src/symfony/src/Security/Authentication/Token/WebauthnToken.php - - message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\PublicKeyCredentialOptions\\:\\:createFromString\\(\\) expects string, mixed given\\.$#" + message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$firewallName \\(string\\) does not accept mixed\\.$#" count: 1 path: src/symfony/src/Security/Authentication/Token/WebauthnToken.php - - message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\PublicKeyCredentialUserEntity\\:\\:createFromString\\(\\) expects string, mixed given\\.$#" + message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$isBackedUp \\(bool\\) does not accept mixed\\.$#" count: 1 path: src/symfony/src/Security/Authentication/Token/WebauthnToken.php - - message: "#^Parameter \\#1 \\$object_or_class of function is_subclass_of expects object\\|string, mixed given\\.$#" + message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$isBackupEligible \\(bool\\) does not accept mixed\\.$#" count: 1 path: src/symfony/src/Security/Authentication/Token/WebauthnToken.php - - message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$firewallName \\(string\\) does not accept mixed\\.$#" + message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$isUserPresent \\(bool\\) does not accept mixed\\.$#" count: 1 path: src/symfony/src/Security/Authentication/Token/WebauthnToken.php - - message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$isBackedUp \\(bool\\) does not accept mixed\\.$#" + message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$isUserVerified \\(bool\\) does not accept mixed\\.$#" count: 1 path: src/symfony/src/Security/Authentication/Token/WebauthnToken.php - - message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$isBackupEligible \\(bool\\) does not accept mixed\\.$#" + message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$publicKeyCredentialDescriptor \\(Webauthn\\\\PublicKeyCredentialDescriptor\\) does not accept mixed\\.$#" count: 1 path: src/symfony/src/Security/Authentication/Token/WebauthnToken.php - - message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$isUserPresent \\(bool\\) does not accept mixed\\.$#" + message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$publicKeyCredentialOptions \\(Webauthn\\\\PublicKeyCredentialOptions\\) does not accept mixed\\.$#" count: 1 path: src/symfony/src/Security/Authentication/Token/WebauthnToken.php - - message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$isUserVerified \\(bool\\) does not accept mixed\\.$#" + message: "#^Property Webauthn\\\\Bundle\\\\Security\\\\Authentication\\\\Token\\\\WebauthnToken\\:\\:\\$publicKeyCredentialUserEntity \\(Webauthn\\\\PublicKeyCredentialUserEntity\\) does not accept mixed\\.$#" count: 1 path: src/symfony/src/Security/Authentication/Token/WebauthnToken.php @@ -1943,9 +2049,14 @@ parameters: path: src/webauthn/src/AttestationStatement/TPMAttestationStatementSupport.php - - message: "#^Method Webauthn\\\\AuthenticationExtensions\\\\AuthenticationExtensionsClientInputs\\:\\:create\\(\\) has parameter \\$extensions with no value type specified in iterable type array\\.$#" + message: "#^@readonly property Webauthn\\\\AuthenticationExtensions\\\\AuthenticationExtensionsClientOutputs\\:\\:\\$extensions is assigned outside of the constructor\\.$#" + count: 1 + path: src/webauthn/src/AuthenticationExtensions/AuthenticationExtensionsClientOutputs.php + + - + message: "#^@readonly property cannot have a default value\\.$#" count: 1 - path: src/webauthn/src/AuthenticationExtensions/AuthenticationExtensionsClientInputs.php + path: src/webauthn/src/AuthenticationExtensions/AuthenticationExtensionsClientOutputs.php - message: "#^Parameter \\#1 \\$json of static method Webauthn\\\\AuthenticationExtensions\\\\AuthenticationExtensionsClientOutputs\\:\\:createFromArray\\(\\) expects array\\, mixed given\\.$#" @@ -2112,6 +2223,271 @@ parameters: count: 1 path: src/webauthn/src/CollectedClientData.php + - + message: "#^Cannot access offset 'authData' on mixed\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AttestationObjectDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AttestationObjectDenormalizer\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AttestationObjectDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AttestationObjectDenormalizer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AttestationObjectDenormalizer.php + + - + message: "#^Parameter \\#1 \\$data of class Webauthn\\\\StringStream constructor expects string, mixed given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AttestationObjectDenormalizer.php + + - + message: "#^Cannot access offset 'fmt' on mixed\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AttestationStatementDenormalizer\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AttestationStatementDenormalizer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php + + - + message: "#^Parameter \\#1 \\$attestation of method Webauthn\\\\AttestationStatement\\\\AttestationStatementSupport\\:\\:load\\(\\) expects array\\, mixed given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php + + - + message: "#^Parameter \\#1 \\$name of method Webauthn\\\\AttestationStatement\\\\AttestationStatementSupportManager\\:\\:get\\(\\) expects string, mixed given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticationExtensionsClientInputsDenormalizer\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticationExtensionsClientInputsDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticationExtensionsClientInputsDenormalizer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticationExtensionsClientInputsDenormalizer.php + + - + message: "#^Cannot access offset 'authenticatorData' on mixed\\.$#" + count: 2 + path: src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php + + - + message: "#^Cannot access offset 'clientDataJSON' on mixed\\.$#" + count: 2 + path: src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php + + - + message: "#^Cannot access offset 'signature' on mixed\\.$#" + count: 2 + path: src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php + + - + message: "#^Cannot access offset 'userHandle' on mixed\\.$#" + count: 2 + path: src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticatorAssertionResponseDenormalizer\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticatorAssertionResponseDenormalizer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php + + - + message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\Util\\\\Base64\\:\\:decode\\(\\) expects string, mixed given\\.$#" + count: 3 + path: src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php + + - + message: "#^Parameter \\#1 \\$encodedString of static method ParagonIE\\\\ConstantTime\\\\Base64\\:\\:decodeNoPadding\\(\\) expects string, mixed given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php + + - + message: "#^Cannot access offset 'attestationObject' on mixed\\.$#" + count: 2 + path: src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php + + - + message: "#^Cannot access offset 'clientDataJSON' on mixed\\.$#" + count: 2 + path: src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticatorAttestationResponseDenormalizer\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticatorAttestationResponseDenormalizer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php + + - + message: "#^Parameter \\#1 \\$data of static method Webauthn\\\\Util\\\\Base64\\:\\:decode\\(\\) expects string, mixed given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php + + - + message: "#^Parameter \\#1 \\$encodedString of static method ParagonIE\\\\ConstantTime\\\\Base64\\:\\:decodeNoPadding\\(\\) expects string, mixed given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php + + - + message: "#^Cannot access offset 1 on array\\|false\\.$#" + count: 2 + path: src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticatorDataDenormalizer\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticatorDataDenormalizer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php + + - + message: "#^Parameter \\#1 \\$data of method Webauthn\\\\Denormalizer\\\\AuthenticatorDataDenormalizer\\:\\:fixIncorrectEdDSAKey\\(\\) expects string, mixed given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php + + - + message: "#^Parameter \\#1 \\$search of function str_replace expects array\\|string, string\\|false given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php + + - + message: "#^Parameter \\#2 \\$callback of function array_reduce expects callable\\(string, mixed\\)\\: string, Closure\\(string, string\\)\\: non\\-empty\\-string given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php + + - + message: "#^Parameter \\#2 \\$needle of function mb_strpos expects string, string\\|false given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php + + - + message: "#^Parameter \\#2 \\$replace of function str_replace expects array\\|string, string\\|false given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticatorResponseDenormalizer\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorResponseDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\AuthenticatorResponseDenormalizer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/AuthenticatorResponseDenormalizer.php + + - + message: "#^Parameter \\#2 \\$array of function array_key_exists expects array, mixed given\\.$#" + count: 2 + path: src/webauthn/src/Denormalizer/AuthenticatorResponseDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\CollectedClientDataDenormalizer\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/CollectedClientDataDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\CollectedClientDataDenormalizer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/CollectedClientDataDenormalizer.php + + - + message: "#^Parameter \\#1 \\$json of function json_decode expects string, mixed given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/CollectedClientDataDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialDenormalizer\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialDenormalizer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialDenormalizer.php + + - + message: "#^Parameter \\#2 \\$array of function array_key_exists expects array, mixed given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialOptionsDenormalizer\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialOptionsDenormalizer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php + + - + message: "#^Parameter \\#2 \\$array of function array_key_exists expects array, mixed given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php + + - + message: "#^Cannot access offset 'credentialPublicKey'\\|'publicKeyCredential…'\\|'userHandle' on mixed\\.$#" + count: 2 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialSourceDenormalizer\\:\\:denormalize\\(\\) 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\\:\\:supportsDenormalization\\(\\) 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 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php + + - + message: "#^Parameter \\#2 \\$array of function array_key_exists expects array, mixed given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialUserEntityDenormalizer\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialUserEntityDenormalizer.php + + - + message: "#^Method Webauthn\\\\Denormalizer\\\\PublicKeyCredentialUserEntityDenormalizer\\:\\:supportsDenormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialUserEntityDenormalizer.php + + - + message: "#^Parameter \\#2 \\$array of function array_key_exists expects array, mixed given\\.$#" + count: 1 + path: src/webauthn/src/Denormalizer/PublicKeyCredentialUserEntityDenormalizer.php + - message: "#^@readonly property Webauthn\\\\PublicKeyCredentialCreationOptions\\:\\:\\$attestation is assigned outside of the constructor\\.$#" count: 1 @@ -2167,6 +2543,14 @@ parameters: count: 1 path: src/webauthn/src/PublicKeyCredentialDescriptorCollection.php + - + message: """ + #^Call to deprecated method loadArray\\(\\) of class Webauthn\\\\PublicKeyCredentialLoader\\: + since 4\\.8\\.0 and will be removed in 5\\.0\\.0\\. Please use \\{self\\:\\:load\\} instead$# + """ + count: 1 + path: src/webauthn/src/PublicKeyCredentialLoader.php + - message: "#^Parameter \\#1 \\$json of method Webauthn\\\\PublicKeyCredentialLoader\\:\\:loadArray\\(\\) expects array, mixed given\\.$#" count: 1 diff --git a/src/metadata-service/composer.json b/src/metadata-service/composer.json index baa25ba6..48b42c9b 100644 --- a/src/metadata-service/composer.json +++ b/src/metadata-service/composer.json @@ -44,6 +44,8 @@ } }, "suggest": { + "symfony/serializer": "As of 4.5.x, the symfony/serializer component will become mandatory for converting objects such as the Metadata Statement", + "phpdocumentor/reflection-docblock": "As of 4.5.x, the phpdocumentor/reflection-docblock component will become mandatory for converting objects such as the Metadata Statement", "psr/clock-implementation": "As of 4.5.x, the PSR Clock implementation will replace lcobucci/clock", "psr/log-implementation": "Recommended to receive logs from the library", "web-token/jwt-key-mgmt": "Mandatory for fetching Metadata Statement from distant sources", diff --git a/src/metadata-service/src/CertificateChain/CertificateToolbox.php b/src/metadata-service/src/CertificateChain/CertificateToolbox.php index edd0d613..a4a586db 100644 --- a/src/metadata-service/src/CertificateChain/CertificateToolbox.php +++ b/src/metadata-service/src/CertificateChain/CertificateToolbox.php @@ -37,6 +37,7 @@ public static function fixPEMStructure(string $data, string $type = 'CERTIFICATE /** * @deprecated since 4.7.0 and will be removed in 5.0.0. No replacement as not used internally. + * @infection-ignore-all */ public static function convertPEMToDER(string $data): string { diff --git a/src/metadata-service/src/Denormalizer/ExtensionDescriptorDenormalizer.php b/src/metadata-service/src/Denormalizer/ExtensionDescriptorDenormalizer.php new file mode 100644 index 00000000..640568d5 --- /dev/null +++ b/src/metadata-service/src/Denormalizer/ExtensionDescriptorDenormalizer.php @@ -0,0 +1,54 @@ +denormalizer === null) { + throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); + } + + if (array_key_exists('fail_if_unknown', $data)) { + $data['failIfUnknown'] = $data['fail_if_unknown']; + unset($data['fail_if_unknown']); + } + + $context[self::ALREADY_CALLED] = true; + + return $this->denormalizer->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + { + if ($context[self::ALREADY_CALLED] ?? false) { + return false; + } + + return $type === ExtensionDescriptor::class; + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + return [ + ExtensionDescriptor::class => false, + ]; + } +} diff --git a/src/metadata-service/src/Denormalizer/MetadataStatementSerializerFactory.php b/src/metadata-service/src/Denormalizer/MetadataStatementSerializerFactory.php new file mode 100644 index 00000000..94ea2ca8 --- /dev/null +++ b/src/metadata-service/src/Denormalizer/MetadataStatementSerializerFactory.php @@ -0,0 +1,58 @@ + $package) { + if (! class_exists($class)) { + return null; + } + } + + $denormalizers = [ + new ExtensionDescriptorDenormalizer(), + new UidNormalizer(), + new ArrayDenormalizer(), + new ObjectNormalizer( + propertyTypeExtractor: new PropertyInfoExtractor(typeExtractors: [ + new PhpDocExtractor(), + new ReflectionExtractor(), + ]) + ), + ]; + + return new Serializer($denormalizers, [new JsonEncoder()]); + } + + /** + * @return array + */ + private static function getRequiredSerializerClasses(): array + { + return [ + UidNormalizer::class => 'symfony/serializer', + ArrayDenormalizer::class => 'symfony/serializer', + ObjectNormalizer::class => 'symfony/serializer', + PropertyInfoExtractor::class => 'symfony/serializer', + PhpDocExtractor::class => 'phpdocumentor/reflection-docblock', + ReflectionExtractor::class => 'symfony/serializer', + JsonEncoder::class => 'symfony/serializer', + Serializer::class => 'symfony/serializer', + ]; + } +} diff --git a/src/metadata-service/src/Service/DistantResourceMetadataService.php b/src/metadata-service/src/Service/DistantResourceMetadataService.php index 5a507e5a..98eec3a7 100644 --- a/src/metadata-service/src/Service/DistantResourceMetadataService.php +++ b/src/metadata-service/src/Service/DistantResourceMetadataService.php @@ -8,7 +8,9 @@ use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; +use Symfony\Component\Serializer\SerializerInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Webauthn\MetadataService\Denormalizer\MetadataStatementSerializerFactory; use Webauthn\MetadataService\Event\CanDispatchEvents; use Webauthn\MetadataService\Event\MetadataStatementFound; use Webauthn\MetadataService\Event\NullEventDispatcher; @@ -23,6 +25,8 @@ final class DistantResourceMetadataService implements MetadataService, CanDispat private EventDispatcherInterface $dispatcher; + private readonly ?SerializerInterface $serializer; + /** * @param array $additionalHeaderParameters */ @@ -32,6 +36,7 @@ public function __construct( private readonly string $uri, private readonly bool $isBase64Encoded = false, private readonly array $additionalHeaderParameters = [], + ?SerializerInterface $serializer = null, ) { if ($requestFactory !== null && ! $httpClient instanceof HttpClientInterface) { trigger_deprecation( @@ -40,6 +45,7 @@ public function __construct( 'The parameter "$requestFactory" will be removed in 5.0.0. Please set it to null and set an Symfony\Contracts\HttpClient\HttpClientInterface as "$httpClient" argument.' ); } + $this->serializer = $serializer ?? MetadataStatementSerializerFactory::create(); $this->dispatcher = new NullEventDispatcher(); } @@ -56,9 +62,10 @@ public static function create( ClientInterface|HttpClientInterface $httpClient, string $uri, bool $isBase64Encoded = false, - array $additionalHeaderParameters = [] + array $additionalHeaderParameters = [], + ?SerializerInterface $serializer = null ): self { - return new self($requestFactory, $httpClient, $uri, $isBase64Encoded, $additionalHeaderParameters); + return new self($requestFactory, $httpClient, $uri, $isBase64Encoded, $additionalHeaderParameters, $serializer); } public function list(): iterable @@ -105,6 +112,11 @@ private function loadData(): void if ($this->isBase64Encoded) { $content = Base64::decode($content, true); } + if ($this->serializer !== null) { + $this->statement = $this->serializer->deserialize($content, MetadataStatement::class, 'json'); + return; + } + $this->statement = MetadataStatement::createFromString($content); } diff --git a/src/metadata-service/src/Service/FidoAllianceCompliantMetadataService.php b/src/metadata-service/src/Service/FidoAllianceCompliantMetadataService.php index 91272cc4..bcef7c18 100644 --- a/src/metadata-service/src/Service/FidoAllianceCompliantMetadataService.php +++ b/src/metadata-service/src/Service/FidoAllianceCompliantMetadataService.php @@ -13,10 +13,12 @@ use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; +use Symfony\Component\Serializer\SerializerInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; use Throwable; use Webauthn\MetadataService\CertificateChain\CertificateChainValidator; use Webauthn\MetadataService\CertificateChain\CertificateToolbox; +use Webauthn\MetadataService\Denormalizer\MetadataStatementSerializerFactory; use Webauthn\MetadataService\Event\CanDispatchEvents; use Webauthn\MetadataService\Event\MetadataStatementFound; use Webauthn\MetadataService\Event\NullEventDispatcher; @@ -45,6 +47,8 @@ final class FidoAllianceCompliantMetadataService implements MetadataService, Can private EventDispatcherInterface $dispatcher; + private readonly ?SerializerInterface $serializer; + /** * @param array $additionalHeaderParameters */ @@ -55,6 +59,7 @@ public function __construct( private readonly array $additionalHeaderParameters = [], private readonly ?CertificateChainValidator $certificateChainValidator = null, private readonly ?string $rootCertificateUri = null, + ?SerializerInterface $serializer = null, ) { if ($requestFactory !== null && ! $httpClient instanceof HttpClientInterface) { trigger_deprecation( @@ -63,6 +68,7 @@ public function __construct( 'The parameter "$requestFactory" will be removed in 5.0.0. Please set it to null and set an Symfony\Contracts\HttpClient\HttpClientInterface as "$httpClient" argument.' ); } + $this->serializer = $serializer ?? MetadataStatementSerializerFactory::create(); $this->dispatcher = new NullEventDispatcher(); } @@ -81,6 +87,7 @@ public static function create( array $additionalHeaderParameters = [], ?CertificateChainValidator $certificateChainValidator = null, ?string $rootCertificateUri = null, + ?SerializerInterface $serializer = null, ): self { return new self( $requestFactory, @@ -88,7 +95,8 @@ public static function create( $uri, $additionalHeaderParameters, $certificateChainValidator, - $rootCertificateUri + $rootCertificateUri, + $serializer, ); } @@ -139,8 +147,20 @@ private function loadData(): void $jwtCertificates = []; try { $payload = $this->getJwsPayload($content, $jwtCertificates); - $data = json_decode($payload, true, flags: JSON_THROW_ON_ERROR); $this->validateCertificates(...$jwtCertificates); + if ($this->serializer !== null) { + $blob = $this->serializer->deserialize($payload, MetadataBLOBPayload::class, 'json'); + foreach ($blob->entries as $entry) { + $mds = $entry->metadataStatement; + if ($mds !== null && $entry->aaguid !== null) { + $this->statements[$entry->aaguid] = $mds; + $this->statusReports[$entry->aaguid] = $entry->statusReports; + } + } + $this->loaded = true; + return; + } + $data = json_decode($payload, true, flags: JSON_THROW_ON_ERROR); foreach ($data['entries'] as $datum) { $entry = MetadataBLOBPayloadEntry::createFromArray($datum); diff --git a/src/metadata-service/src/Service/FolderResourceMetadataService.php b/src/metadata-service/src/Service/FolderResourceMetadataService.php index ee70fa75..c14f5ece 100644 --- a/src/metadata-service/src/Service/FolderResourceMetadataService.php +++ b/src/metadata-service/src/Service/FolderResourceMetadataService.php @@ -5,6 +5,8 @@ namespace Webauthn\MetadataService\Service; use InvalidArgumentException; +use Symfony\Component\Serializer\SerializerInterface; +use Webauthn\MetadataService\Denormalizer\MetadataStatementSerializerFactory; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; use Webauthn\MetadataService\Statement\MetadataStatement; use function file_get_contents; @@ -14,10 +16,13 @@ final class FolderResourceMetadataService implements MetadataService { - private readonly string $rootPath; + private readonly ?SerializerInterface $serializer; - public function __construct(string $rootPath) - { + public function __construct( + private string $rootPath, + ?SerializerInterface $serializer = null, + ) { + $this->serializer = $serializer ?? MetadataStatementSerializerFactory::create(); $this->rootPath = rtrim($rootPath, DIRECTORY_SEPARATOR); is_dir($this->rootPath) || throw new InvalidArgumentException('The given parameter is not a valid folder.'); is_readable($this->rootPath) || throw new InvalidArgumentException( @@ -25,6 +30,11 @@ public function __construct(string $rootPath) ); } + public static function create(string $rootPath, ?SerializerInterface $serializer = null): self + { + return new self($rootPath, $serializer); + } + public function list(): iterable { $files = glob($this->rootPath . DIRECTORY_SEPARATOR . '*'); @@ -53,7 +63,12 @@ public function get(string $aaguid): MetadataStatement )); $filename = $this->rootPath . DIRECTORY_SEPARATOR . $aaguid; $data = trim(file_get_contents($filename)); - $mds = MetadataStatement::createFromString($data); + if ($this->serializer !== null) { + $mds = $this->serializer->deserialize($data, MetadataStatement::class, 'json'); + } else { + $mds = MetadataStatement::createFromString($data); + } + $mds->aaguid !== null || throw MetadataStatementLoadingException::create('Invalid Metadata Statement.'); return $mds; diff --git a/src/metadata-service/src/Service/JsonMetadataService.php b/src/metadata-service/src/Service/JsonMetadataService.php new file mode 100644 index 00000000..7aa57e81 --- /dev/null +++ b/src/metadata-service/src/Service/JsonMetadataService.php @@ -0,0 +1,78 @@ +dispatcher = new NullEventDispatcher(); + $this->serializer = $serializer ?? MetadataStatementSerializerFactory::create(); + foreach ($statements as $statement) { + $this->addStatement($statement); + } + } + + public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): void + { + $this->dispatcher = $eventDispatcher; + } + + public function list(): iterable + { + yield from array_keys($this->statements); + } + + public function has(string $aaguid): bool + { + return array_key_exists($aaguid, $this->statements); + } + + public function get(string $aaguid): MetadataStatement + { + array_key_exists($aaguid, $this->statements) || throw MissingMetadataStatementException::create($aaguid); + $mds = $this->statements[$aaguid]; + $this->dispatcher->dispatch(MetadataStatementFound::create($mds)); + + return $mds; + } + + private function addStatement(string $statement): void + { + if ($this->serializer === null) { + $mds = MetadataStatement::createFromString($statement); + } else { + $mds = $this->serializer->deserialize($statement, MetadataStatement::class, 'json'); + } + if ($mds->aaguid === null) { + return; + } + $this->statements[$mds->aaguid] = $mds; + } +} diff --git a/src/metadata-service/src/Service/LocalResourceMetadataService.php b/src/metadata-service/src/Service/LocalResourceMetadataService.php index 0d677a7b..b522c2a2 100644 --- a/src/metadata-service/src/Service/LocalResourceMetadataService.php +++ b/src/metadata-service/src/Service/LocalResourceMetadataService.php @@ -6,6 +6,8 @@ use ParagonIE\ConstantTime\Base64; use Psr\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Serializer\SerializerInterface; +use Webauthn\MetadataService\Denormalizer\MetadataStatementSerializerFactory; use Webauthn\MetadataService\Event\CanDispatchEvents; use Webauthn\MetadataService\Event\MetadataStatementFound; use Webauthn\MetadataService\Event\NullEventDispatcher; @@ -20,21 +22,28 @@ final class LocalResourceMetadataService implements MetadataService, CanDispatch private EventDispatcherInterface $dispatcher; + private readonly ?SerializerInterface $serializer; + public function __construct( private readonly string $filename, private readonly bool $isBase64Encoded = false, + ?SerializerInterface $serializer = null, ) { + $this->serializer = $serializer ?? MetadataStatementSerializerFactory::create(); $this->dispatcher = new NullEventDispatcher(); } - public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): void - { - $this->dispatcher = $eventDispatcher; + public static function create( + string $filename, + bool $isBase64Encoded = false, + ?SerializerInterface $serializer = null + ): self { + return new self($filename, $isBase64Encoded, $serializer); } - public static function create(string $filename, bool $isBase64Encoded = false): self + public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): void { - return new self($filename, $isBase64Encoded); + $this->dispatcher = $eventDispatcher; } public function list(): iterable @@ -81,6 +90,10 @@ private function loadData(): void if ($this->isBase64Encoded) { $content = Base64::decode($content, true); } - $this->statement = MetadataStatement::createFromString($content); + if ($this->serializer !== null) { + $this->statement = $this->serializer->deserialize($content, MetadataStatement::class, 'json'); + } else { + $this->statement = MetadataStatement::createFromString($content); + } } } diff --git a/src/metadata-service/src/Service/MetadataBLOBPayload.php b/src/metadata-service/src/Service/MetadataBLOBPayload.php index 1e160d09..3ab82458 100644 --- a/src/metadata-service/src/Service/MetadataBLOBPayload.php +++ b/src/metadata-service/src/Service/MetadataBLOBPayload.php @@ -6,7 +6,7 @@ use JsonSerializable; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; -use Webauthn\MetadataService\Utils; +use Webauthn\MetadataService\ValueFilter; use function array_key_exists; use function is_array; use function is_int; @@ -14,6 +14,8 @@ class MetadataBLOBPayload implements JsonSerializable { + use ValueFilter; + /** * @var string[] */ @@ -32,6 +34,7 @@ public function __construct( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function addEntry(MetadataBLOBPayloadEntry $entry): self { @@ -42,6 +45,7 @@ public function addEntry(MetadataBLOBPayloadEntry $entry): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getLegalHeader(): ?string { @@ -50,6 +54,7 @@ public function getLegalHeader(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getNo(): int { @@ -58,6 +63,7 @@ public function getNo(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getNextUpdate(): string { @@ -67,6 +73,7 @@ public function getNextUpdate(): string /** * @return MetadataBLOBPayloadEntry[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getEntries(): array { @@ -75,10 +82,12 @@ public function getEntries(): array /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { - $data = Utils::filterNullValues($data); + $data = self::filterNullValues($data); foreach (['no', 'nextUpdate', 'entries'] as $key) { array_key_exists($key, $data) || throw MetadataStatementLoadingException::create(sprintf( 'Invalid data. The parameter "%s" is missing', @@ -114,12 +123,13 @@ public function jsonSerialize(): array 'entries' => $this->entries, ]; - return Utils::filterNullValues($data); + return self::filterNullValues($data); } /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getRootCertificates(): array { @@ -129,6 +139,7 @@ public function getRootCertificates(): array /** * @param string[] $rootCertificates * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function setRootCertificates(array $rootCertificates): self { diff --git a/src/metadata-service/src/Service/MetadataBLOBPayloadEntry.php b/src/metadata-service/src/Service/MetadataBLOBPayloadEntry.php index 2b429abd..575129a2 100644 --- a/src/metadata-service/src/Service/MetadataBLOBPayloadEntry.php +++ b/src/metadata-service/src/Service/MetadataBLOBPayloadEntry.php @@ -9,7 +9,7 @@ use Webauthn\MetadataService\Statement\BiometricStatusReport; use Webauthn\MetadataService\Statement\MetadataStatement; use Webauthn\MetadataService\Statement\StatusReport; -use Webauthn\MetadataService\Utils; +use Webauthn\MetadataService\ValueFilter; use function array_key_exists; use function count; use function is_array; @@ -17,32 +17,23 @@ class MetadataBLOBPayloadEntry implements JsonSerializable { - /** - * @var string[] - */ - public array $attestationCertificateKeyIdentifiers = []; - - /** - * @var BiometricStatusReport[] - */ - public array $biometricStatusReports = []; - - /** - * @var StatusReport[] - */ - public array $statusReports = []; + use ValueFilter; /** + * @param StatusReport[] $statusReports + * @param BiometricStatusReport[] $biometricStatusReports * @param string[] $attestationCertificateKeyIdentifiers */ public function __construct( - public readonly ?string $aaid, - public readonly ?string $aaguid, - array $attestationCertificateKeyIdentifiers, - public readonly ?MetadataStatement $metadataStatement, public readonly string $timeOfLastStatusChange, - public readonly ?string $rogueListURL, - public readonly ?string $rogueListHash + public array $statusReports, + public readonly ?string $aaid = null, + public readonly ?string $aaguid = null, + public array $attestationCertificateKeyIdentifiers = [], + public readonly ?MetadataStatement $metadataStatement = null, + public readonly ?string $rogueListURL = null, + public readonly ?string $rogueListHash = null, + public array $biometricStatusReports = [] ) { if ($aaid !== null && $aaguid !== null) { throw MetadataStatementLoadingException::create('Authenticators cannot support both AAID and AAGUID'); @@ -68,6 +59,7 @@ public function __construct( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAaid(): ?string { @@ -76,6 +68,7 @@ public function getAaid(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAaguid(): ?string { @@ -85,6 +78,7 @@ public function getAaguid(): ?string /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAttestationCertificateKeyIdentifiers(): array { @@ -93,6 +87,7 @@ public function getAttestationCertificateKeyIdentifiers(): array /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getMetadataStatement(): ?MetadataStatement { @@ -101,6 +96,7 @@ public function getMetadataStatement(): ?MetadataStatement /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function addBiometricStatusReports(BiometricStatusReport ...$biometricStatusReports): self { @@ -114,6 +110,7 @@ public function addBiometricStatusReports(BiometricStatusReport ...$biometricSta /** * @return BiometricStatusReport[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getBiometricStatusReports(): array { @@ -122,6 +119,7 @@ public function getBiometricStatusReports(): array /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function addStatusReports(StatusReport ...$statusReports): self { @@ -135,6 +133,7 @@ public function addStatusReports(StatusReport ...$statusReports): self /** * @return StatusReport[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getStatusReports(): array { @@ -143,6 +142,7 @@ public function getStatusReports(): array /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getTimeOfLastStatusChange(): string { @@ -151,6 +151,7 @@ public function getTimeOfLastStatusChange(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getRogueListURL(): string|null { @@ -159,6 +160,7 @@ public function getRogueListURL(): string|null /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getRogueListHash(): string|null { @@ -167,10 +169,12 @@ public function getRogueListHash(): string|null /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { - $data = Utils::filterNullValues($data); + $data = self::filterNullValues($data); array_key_exists('timeOfLastStatusChange', $data) || throw MetadataStatementLoadingException::create( 'Invalid data. The parameter "timeOfLastStatusChange" is missing' ); @@ -180,25 +184,26 @@ public static function createFromArray(array $data): self is_array($data['statusReports']) || throw MetadataStatementLoadingException::create( 'Invalid data. The parameter "statusReports" shall be an array of StatusReport objects' ); - $object = new self( + + return new self( + $data['timeOfLastStatusChange'], + array_map( + static fn (array $statusReport) => StatusReport::createFromArray($statusReport), + $data['statusReports'] + ), $data['aaid'] ?? null, $data['aaguid'] ?? null, $data['attestationCertificateKeyIdentifiers'] ?? [], isset($data['metadataStatement']) ? MetadataStatement::createFromArray($data['metadataStatement']) : null, - $data['timeOfLastStatusChange'], $data['rogueListURL'] ?? null, - $data['rogueListHash'] ?? null + $data['rogueListHash'] ?? null, + array_map( + static fn (array $biometricStatusReport) => BiometricStatusReport::createFromArray( + $biometricStatusReport + ), + $data['biometricStatusReports'] ?? [] + ) ); - foreach ($data['statusReports'] as $statusReport) { - $object->statusReports[] = StatusReport::createFromArray($statusReport); - } - if (array_key_exists('biometricStatusReport', $data)) { - foreach ($data['biometricStatusReport'] as $biometricStatusReport) { - $object->biometricStatusReports[] = BiometricStatusReport::createFromArray($biometricStatusReport); - } - } - - return $object; } /** @@ -216,6 +221,6 @@ public function jsonSerialize(): array 'rogueListHash' => $this->rogueListHash, ]; - return Utils::filterNullValues($data); + return self::filterNullValues($data); } } diff --git a/src/metadata-service/src/Service/StringMetadataService.php b/src/metadata-service/src/Service/StringMetadataService.php index 11cfd6c5..4edea340 100644 --- a/src/metadata-service/src/Service/StringMetadataService.php +++ b/src/metadata-service/src/Service/StringMetadataService.php @@ -12,6 +12,10 @@ use Webauthn\MetadataService\Statement\MetadataStatement; use function array_key_exists; +/** + * @deprecated since 4.8.0 and will be removed in 5.0.0. Please use Webauthn\MetadataService\Service\JsonMetadataService instead. + * @infection-ignore-all + */ final class StringMetadataService implements MetadataService, CanDispatchEvents { /** diff --git a/src/metadata-service/src/Statement/AbstractDescriptor.php b/src/metadata-service/src/Statement/AbstractDescriptor.php index 7de4eb2c..08f1de46 100644 --- a/src/metadata-service/src/Statement/AbstractDescriptor.php +++ b/src/metadata-service/src/Statement/AbstractDescriptor.php @@ -23,6 +23,7 @@ public function __construct( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getMaxRetries(): ?int { @@ -31,6 +32,7 @@ public function getMaxRetries(): ?int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getBlockSlowdown(): ?int { diff --git a/src/metadata-service/src/Statement/AlternativeDescriptions.php b/src/metadata-service/src/Statement/AlternativeDescriptions.php index b5545f42..228811e0 100644 --- a/src/metadata-service/src/Statement/AlternativeDescriptions.php +++ b/src/metadata-service/src/Statement/AlternativeDescriptions.php @@ -27,6 +27,7 @@ public static function create(array $descriptions = []): self /** * @return array * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function all(): array { @@ -35,6 +36,7 @@ public function all(): array /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function add(string $locale, string $description): self { diff --git a/src/metadata-service/src/Statement/AuthenticatorGetInfo.php b/src/metadata-service/src/Statement/AuthenticatorGetInfo.php index 1e032a13..e4b5c5a6 100644 --- a/src/metadata-service/src/Statement/AuthenticatorGetInfo.php +++ b/src/metadata-service/src/Statement/AuthenticatorGetInfo.php @@ -12,20 +12,22 @@ class AuthenticatorGetInfo implements JsonSerializable * @param array $info */ public function __construct( + /** @readonly */ public array $info = [] ) { } /** - * @param array $data + * @param array $info */ - public static function create(array $data = []): self + public static function create(array $info = []): self { - return new self($data); + return new self($info); } /** - * @deprecated since 4.7.0. Please use the property directly. + * @deprecated since 4.7.0. Please use the constructor directly. + * @infection-ignore-all */ public function add(string|int $key, mixed $value): self { diff --git a/src/metadata-service/src/Statement/AuthenticatorStatus.php b/src/metadata-service/src/Statement/AuthenticatorStatus.php index 71471501..03a008be 100644 --- a/src/metadata-service/src/Statement/AuthenticatorStatus.php +++ b/src/metadata-service/src/Statement/AuthenticatorStatus.php @@ -63,6 +63,7 @@ final class AuthenticatorStatus /** * @return string[] * @deprecated since 4.7.0. Please use the constant STATUSES instead. + * @infection-ignore-all */ public static function list(): array { diff --git a/src/metadata-service/src/Statement/BiometricAccuracyDescriptor.php b/src/metadata-service/src/Statement/BiometricAccuracyDescriptor.php index 85026d14..14c0e34d 100644 --- a/src/metadata-service/src/Statement/BiometricAccuracyDescriptor.php +++ b/src/metadata-service/src/Statement/BiometricAccuracyDescriptor.php @@ -4,10 +4,12 @@ namespace Webauthn\MetadataService\Statement; -use Webauthn\MetadataService\Utils; +use Webauthn\MetadataService\ValueFilter; class BiometricAccuracyDescriptor extends AbstractDescriptor { + use ValueFilter; + public function __construct( public readonly ?float $selfAttestedFRR, public readonly ?float $selfAttestedFAR, @@ -54,6 +56,7 @@ public function getMaxTemplates(): ?float /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. */ public static function createFromArray(array $data): self { @@ -79,6 +82,6 @@ public function jsonSerialize(): array 'blockSlowdown' => $this->blockSlowdown, ]; - return Utils::filterNullValues($data); + return self::filterNullValues($data); } } diff --git a/src/metadata-service/src/Statement/BiometricStatusReport.php b/src/metadata-service/src/Statement/BiometricStatusReport.php index a2256e67..a6b0f3a9 100644 --- a/src/metadata-service/src/Statement/BiometricStatusReport.php +++ b/src/metadata-service/src/Statement/BiometricStatusReport.php @@ -41,6 +41,7 @@ public static function create( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCertLevel(): int|null { @@ -49,6 +50,7 @@ public function getCertLevel(): int|null /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getModality(): int|null { @@ -57,6 +59,7 @@ public function getModality(): int|null /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getEffectiveDate(): ?string { @@ -65,6 +68,7 @@ public function getEffectiveDate(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCertificationDescriptor(): ?string { @@ -73,6 +77,7 @@ public function getCertificationDescriptor(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCertificateNumber(): ?string { @@ -81,6 +86,7 @@ public function getCertificateNumber(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCertificationPolicyVersion(): ?string { @@ -89,6 +95,7 @@ public function getCertificationPolicyVersion(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCertificationRequirementsVersion(): ?string { @@ -97,6 +104,8 @@ public function getCertificationRequirementsVersion(): ?string /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { diff --git a/src/metadata-service/src/Statement/CodeAccuracyDescriptor.php b/src/metadata-service/src/Statement/CodeAccuracyDescriptor.php index 5c43b434..cb15d760 100644 --- a/src/metadata-service/src/Statement/CodeAccuracyDescriptor.php +++ b/src/metadata-service/src/Statement/CodeAccuracyDescriptor.php @@ -5,11 +5,13 @@ namespace Webauthn\MetadataService\Statement; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; -use Webauthn\MetadataService\Utils; +use Webauthn\MetadataService\ValueFilter; use function array_key_exists; class CodeAccuracyDescriptor extends AbstractDescriptor { + use ValueFilter; + public function __construct( public readonly int $base, public readonly int $minLength, @@ -32,6 +34,7 @@ public static function create(int $base, int $minLength, ?int $maxRetries = null /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getBase(): int { @@ -40,6 +43,7 @@ public function getBase(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getMinLength(): int { @@ -48,6 +52,8 @@ public function getMinLength(): int /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { @@ -78,6 +84,6 @@ public function jsonSerialize(): array 'blockSlowdown' => $this->blockSlowdown, ]; - return Utils::filterNullValues($data); + return self::filterNullValues($data); } } diff --git a/src/metadata-service/src/Statement/DisplayPNGCharacteristicsDescriptor.php b/src/metadata-service/src/Statement/DisplayPNGCharacteristicsDescriptor.php index 0e1f533b..d6099e37 100644 --- a/src/metadata-service/src/Statement/DisplayPNGCharacteristicsDescriptor.php +++ b/src/metadata-service/src/Statement/DisplayPNGCharacteristicsDescriptor.php @@ -6,13 +6,15 @@ use JsonSerializable; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; -use Webauthn\MetadataService\Utils; +use Webauthn\MetadataService\ValueFilter; use function array_key_exists; class DisplayPNGCharacteristicsDescriptor implements JsonSerializable { + use ValueFilter; + /** - * @param RgbPaletteEntry[] $width + * @param RgbPaletteEntry[] $plte */ public function __construct( public readonly int $width, @@ -22,7 +24,8 @@ public function __construct( public readonly int $compression, public readonly int $filter, public readonly int $interlace, - public array $plte, + /** @readonly */ + public array $plte = [], ) { $width >= 0 || throw MetadataStatementLoadingException::create('Invalid width'); $height >= 0 || throw MetadataStatementLoadingException::create('Invalid height'); @@ -56,7 +59,8 @@ public static function create( } /** - * @deprecated since 4.7.0. Please use the property directly. + * @deprecated since 4.7.0. Please use {self::create} directly. + * @infection-ignore-all */ public function addPalettes(RgbPaletteEntry ...$rgbPaletteEntries): self { @@ -69,6 +73,7 @@ public function addPalettes(RgbPaletteEntry ...$rgbPaletteEntries): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getWidth(): int { @@ -77,6 +82,7 @@ public function getWidth(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getHeight(): int { @@ -85,6 +91,7 @@ public function getHeight(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getBitDepth(): int { @@ -93,6 +100,7 @@ public function getBitDepth(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getColorType(): int { @@ -101,6 +109,7 @@ public function getColorType(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCompression(): int { @@ -109,6 +118,7 @@ public function getCompression(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getFilter(): int { @@ -117,6 +127,7 @@ public function getFilter(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getInterlace(): int { @@ -126,6 +137,7 @@ public function getInterlace(): int /** * @return RgbPaletteEntry[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getPaletteEntries(): array { @@ -134,10 +146,12 @@ public function getPaletteEntries(): array /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { - $data = Utils::filterNullValues($data); + $data = self::filterNullValues($data); foreach ([ 'width', 'compression', @@ -181,6 +195,6 @@ public function jsonSerialize(): array 'plte' => $this->plte, ]; - return Utils::filterNullValues($data); + return self::filterNullValues($data); } } diff --git a/src/metadata-service/src/Statement/EcdaaTrustAnchor.php b/src/metadata-service/src/Statement/EcdaaTrustAnchor.php index 7739635b..fd426c13 100644 --- a/src/metadata-service/src/Statement/EcdaaTrustAnchor.php +++ b/src/metadata-service/src/Statement/EcdaaTrustAnchor.php @@ -7,14 +7,17 @@ use JsonSerializable; use ParagonIE\ConstantTime\Base64UrlSafe; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; -use Webauthn\MetadataService\Utils; +use Webauthn\MetadataService\ValueFilter; use function array_key_exists; /** * @deprecated since 4.2.0 and will be removed in 5.0.0. The ECDAA Trust Anchor does no longer exist in Webauthn specification. + * @infection-ignore-all */ class EcdaaTrustAnchor implements JsonSerializable { + use ValueFilter; + public function __construct( private readonly string $X, private readonly string $Y, @@ -57,10 +60,11 @@ public function getG1Curve(): string /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. */ public static function createFromArray(array $data): self { - $data = Utils::filterNullValues($data); + $data = self::filterNullValues($data); foreach (['X', 'Y', 'c', 'sx', 'sy', 'G1Curve'] as $key) { array_key_exists($key, $data) || throw MetadataStatementLoadingException::create(sprintf( 'Invalid data. The key "%s" is missing', @@ -92,6 +96,6 @@ public function jsonSerialize(): array 'G1Curve' => $this->G1Curve, ]; - return Utils::filterNullValues($data); + return self::filterNullValues($data); } } diff --git a/src/metadata-service/src/Statement/ExtensionDescriptor.php b/src/metadata-service/src/Statement/ExtensionDescriptor.php index 019b86b8..4b040436 100644 --- a/src/metadata-service/src/Statement/ExtensionDescriptor.php +++ b/src/metadata-service/src/Statement/ExtensionDescriptor.php @@ -6,11 +6,13 @@ use JsonSerializable; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; -use Webauthn\MetadataService\Utils; +use Webauthn\MetadataService\ValueFilter; use function array_key_exists; class ExtensionDescriptor implements JsonSerializable { + use ValueFilter; + public function __construct( public readonly string $id, public readonly ?int $tag, @@ -35,6 +37,7 @@ public static function create( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getId(): string { @@ -43,6 +46,7 @@ public function getId(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getTag(): ?int { @@ -51,6 +55,7 @@ public function getTag(): ?int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getData(): ?string { @@ -59,6 +64,7 @@ public function getData(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function isFailIfUnknown(): bool { @@ -67,10 +73,12 @@ public function isFailIfUnknown(): bool /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { - $data = Utils::filterNullValues($data); + $data = self::filterNullValues($data); array_key_exists('id', $data) || throw MetadataStatementLoadingException::create( 'Invalid data. The parameter "id" is missing' ); @@ -93,6 +101,6 @@ public function jsonSerialize(): array 'fail_if_unknown' => $this->failIfUnknown, ]; - return Utils::filterNullValues($result); + return self::filterNullValues($result); } } diff --git a/src/metadata-service/src/Statement/MetadataStatement.php b/src/metadata-service/src/Statement/MetadataStatement.php index 4bd7ae5e..d5e2a541 100644 --- a/src/metadata-service/src/Statement/MetadataStatement.php +++ b/src/metadata-service/src/Statement/MetadataStatement.php @@ -7,7 +7,7 @@ use JsonSerializable; use Webauthn\MetadataService\CertificateChain\CertificateToolbox; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; -use Webauthn\MetadataService\Utils; +use Webauthn\MetadataService\ValueFilter; use function array_key_exists; use function is_array; use function is_string; @@ -15,6 +15,8 @@ class MetadataStatement implements JsonSerializable { + use ValueFilter; + final public const KEY_PROTECTION_SOFTWARE = 'software'; final public const KEY_PROTECTION_SOFTWARE_INT = 0x0001; @@ -155,6 +157,7 @@ class MetadataStatement implements JsonSerializable /** * @deprecated since 4.2.0 and will be removed in 5.0.0. The ECDAA Trust Anchor does no longer exist in Webauthn specification. + * @infection-ignore-all */ final public const ATTESTATION_ECDAA = 'ecdaa'; @@ -162,55 +165,7 @@ class MetadataStatement implements JsonSerializable final public const ATTESTATION_ANONCA = 'anonca'; - public ?string $legalHeader = null; - - public ?string $aaid = null; - - public ?string $aaguid = null; - - /** - * @var string[] - */ - public array $attestationCertificateKeyIdentifiers = []; - - public AlternativeDescriptions $alternativeDescriptions; - - /** - * @var string[] - */ - public array $keyProtection = []; - - public ?bool $isKeyRestricted = null; - - public ?bool $isFreshUserVerificationRequired = null; - - public ?int $cryptoStrength = null; - - /** - * @var string[] - */ - public array $attachmentHint = []; - - public ?string $tcDisplayContentType = null; - - /** - * @var DisplayPNGCharacteristicsDescriptor[] - */ - public array $tcDisplayPNGCharacteristics = []; - - /** - * @var EcdaaTrustAnchor[] - */ - public array $ecdaaTrustAnchors = []; - - public ?string $icon = null; - - /** - * @var ExtensionDescriptor[] - */ - public array $supportedExtensions = []; - - public null|AuthenticatorGetInfo $authenticatorGetInfo = null; + public readonly AuthenticatorGetInfo $authenticatorGetInfo; /** * @param Version[] $upv @@ -221,6 +176,11 @@ class MetadataStatement implements JsonSerializable * @param string[] $matcherProtection * @param string[] $tcDisplay * @param string[] $attestationRootCertificates + * @param string[] $attestationCertificateKeyIdentifiers + * @param string[] $keyProtection + * @param string[] $attachmentHint + * @param EcdaaTrustAnchor[] $ecdaaTrustAnchors + * @param ExtensionDescriptor[] $supportedExtensions */ public function __construct( public readonly string $description, @@ -235,9 +195,39 @@ public function __construct( public readonly array $matcherProtection, public readonly array $tcDisplay, public readonly array $attestationRootCertificates, + public readonly ?AlternativeDescriptions $alternativeDescriptions = null, + /** @readonly */ + public ?string $legalHeader = null, + /** @readonly */ + public ?string $aaid = null, + /** @readonly */ + public ?string $aaguid = null, + /** @readonly */ + public array $attestationCertificateKeyIdentifiers = [], + /** @readonly */ + public array $keyProtection = [], + /** @readonly */ + public ?bool $isKeyRestricted = null, + /** @readonly */ + public ?bool $isFreshUserVerificationRequired = null, + /** @readonly */ + public ?int $cryptoStrength = null, + /** @readonly */ + public array $attachmentHint = [], + /** @readonly */ + public ?string $tcDisplayContentType = null, + /** @readonly */ + public array $tcDisplayPNGCharacteristics = [], + /** @readonly */ + public array $ecdaaTrustAnchors = [], + /** @readonly */ + public ?string $icon = null, + /** @readonly */ + public array $supportedExtensions = [], + /** @readonly */ + ?AuthenticatorGetInfo $authenticatorGetInfo = null, ) { - $this->alternativeDescriptions = AlternativeDescriptions::create(); - $this->authenticatorGetInfo = AuthenticatorGetInfo::create(); + $this->authenticatorGetInfo = $authenticatorGetInfo ?? AuthenticatorGetInfo::create($attestationTypes); } public static function create( @@ -252,7 +242,23 @@ public static function create( array $userVerificationDetails, array $matcherProtection, array $tcDisplay, - array $attestationRootCertificates + array $attestationRootCertificates, + array $alternativeDescriptions = [], + ?string $legalHeader = null, + ?string $aaid = null, + ?string $aaguid = null, + array $attestationCertificateKeyIdentifiers = [], + array $keyProtection = [], + ?bool $isKeyRestricted = null, + ?bool $isFreshUserVerificationRequired = null, + ?int $cryptoStrength = null, + array $attachmentHint = [], + ?string $tcDisplayContentType = null, + array $tcDisplayPNGCharacteristics = [], + array $ecdaaTrustAnchors = [], + ?string $icon = null, + array $supportedExtensions = [], + ?AuthenticatorGetInfo $authenticatorGetInfo = null, ): self { return new self( $description, @@ -266,10 +272,30 @@ public static function create( $userVerificationDetails, $matcherProtection, $tcDisplay, - $attestationRootCertificates + $attestationRootCertificates, + AlternativeDescriptions::create($alternativeDescriptions), + $legalHeader, + $aaid, + $aaguid, + $attestationCertificateKeyIdentifiers, + $keyProtection, + $isKeyRestricted, + $isFreshUserVerificationRequired, + $cryptoStrength, + $attachmentHint, + $tcDisplayContentType, + $tcDisplayPNGCharacteristics, + $ecdaaTrustAnchors, + $icon, + $supportedExtensions, + $authenticatorGetInfo, ); } + /** + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all + */ public static function createFromString(string $statement): self { $data = json_decode($statement, true, flags: JSON_THROW_ON_ERROR); @@ -279,6 +305,7 @@ public static function createFromString(string $statement): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getLegalHeader(): ?string { @@ -287,6 +314,7 @@ public function getLegalHeader(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAaid(): ?string { @@ -295,6 +323,7 @@ public function getAaid(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAaguid(): ?string { @@ -308,6 +337,7 @@ public function isKeyRestricted(): ?bool /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function isFreshUserVerificationRequired(): ?bool { @@ -316,6 +346,7 @@ public function isFreshUserVerificationRequired(): ?bool /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAuthenticatorGetInfo(): AuthenticatorGetInfo|null { @@ -325,6 +356,7 @@ public function getAuthenticatorGetInfo(): AuthenticatorGetInfo|null /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAttestationCertificateKeyIdentifiers(): array { @@ -333,6 +365,7 @@ public function getAttestationCertificateKeyIdentifiers(): array /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getDescription(): string { @@ -341,6 +374,7 @@ public function getDescription(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAlternativeDescriptions(): AlternativeDescriptions { @@ -349,6 +383,7 @@ public function getAlternativeDescriptions(): AlternativeDescriptions /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAuthenticatorVersion(): int { @@ -357,6 +392,7 @@ public function getAuthenticatorVersion(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getProtocolFamily(): string { @@ -366,6 +402,7 @@ public function getProtocolFamily(): string /** * @return Version[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getUpv(): array { @@ -374,6 +411,7 @@ public function getUpv(): array /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getSchema(): ?int { @@ -383,6 +421,7 @@ public function getSchema(): ?int /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAuthenticationAlgorithms(): array { @@ -392,6 +431,7 @@ public function getAuthenticationAlgorithms(): array /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getPublicKeyAlgAndEncodings(): array { @@ -401,6 +441,7 @@ public function getPublicKeyAlgAndEncodings(): array /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAttestationTypes(): array { @@ -410,6 +451,7 @@ public function getAttestationTypes(): array /** * @return VerificationMethodANDCombinations[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getUserVerificationDetails(): array { @@ -419,6 +461,7 @@ public function getUserVerificationDetails(): array /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getKeyProtection(): array { @@ -428,6 +471,7 @@ public function getKeyProtection(): array /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getMatcherProtection(): array { @@ -436,6 +480,7 @@ public function getMatcherProtection(): array /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCryptoStrength(): ?int { @@ -445,6 +490,7 @@ public function getCryptoStrength(): ?int /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAttachmentHint(): array { @@ -454,6 +500,7 @@ public function getAttachmentHint(): array /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getTcDisplay(): array { @@ -462,6 +509,7 @@ public function getTcDisplay(): array /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getTcDisplayContentType(): ?string { @@ -471,6 +519,7 @@ public function getTcDisplayContentType(): ?string /** * @return DisplayPNGCharacteristicsDescriptor[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getTcDisplayPNGCharacteristics(): array { @@ -480,6 +529,7 @@ public function getTcDisplayPNGCharacteristics(): array /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAttestationRootCertificates(): array { @@ -490,6 +540,7 @@ public function getAttestationRootCertificates(): array * @return EcdaaTrustAnchor[] * * @deprecated since 4.2.0 and will be removed in 5.0.0. The ECDAA Trust Anchor does no longer exist in Webauthn specification. + * @infection-ignore-all */ public function getEcdaaTrustAnchors(): array { @@ -498,6 +549,7 @@ public function getEcdaaTrustAnchors(): array /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getIcon(): ?string { @@ -507,6 +559,7 @@ public function getIcon(): ?string /** * @return ExtensionDescriptor[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getSupportedExtensions(): array { @@ -515,6 +568,8 @@ public function getSupportedExtensions(): array /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { @@ -559,7 +614,7 @@ public static function createFromArray(array $data): self } } - $object = self::create( + return self::create( $data['description'], $data['authenticatorVersion'], $data['protocolFamily'], @@ -583,53 +638,34 @@ public static function createFromArray(array $data): self }, $data['userVerificationDetails']), $data['matcherProtection'], $data['tcDisplay'], - CertificateToolbox::fixPEMStructures($data['attestationRootCertificates']) + CertificateToolbox::fixPEMStructures($data['attestationRootCertificates']), + $data['alternativeDescriptions'] ?? [], + $data['legalHeader'] ?? null, + $data['aaid'] ?? null, + $data['aaguid'] ?? null, + $data['attestationCertificateKeyIdentifiers'] ?? [], + $data['keyProtection'] ?? [], + $data['isKeyRestricted'] ?? null, + $data['isFreshUserVerificationRequired'] ?? null, + $data['cryptoStrength'] ?? null, + $data['attachmentHint'] ?? [], + $data['tcDisplayContentType'] ?? null, + array_map( + static fn (array $data): DisplayPNGCharacteristicsDescriptor => DisplayPNGCharacteristicsDescriptor::createFromArray( + $data + ), + $data['tcDisplayPNGCharacteristics'] ?? [] + ), + $data['ecdaaTrustAnchors'] ?? [], + $data['icon'] ?? null, + array_map( + static fn ($supportedExtension): ExtensionDescriptor => ExtensionDescriptor::createFromArray( + $supportedExtension + ), + $data['supportedExtensions'] ?? [] + ), + isset($data['authenticatorGetInfo']) ? AuthenticatorGetInfo::create($data['authenticatorGetInfo']) : null, ); - - $object->legalHeader = $data['legalHeader'] ?? null; - $object->aaid = $data['aaid'] ?? null; - $object->aaguid = $data['aaguid'] ?? null; - $object->attestationCertificateKeyIdentifiers = $data['attestationCertificateKeyIdentifiers'] ?? []; - $object->alternativeDescriptions = AlternativeDescriptions::create($data['alternativeDescriptions'] ?? []); - $object->authenticatorGetInfo = isset($data['attestationTypes']) ? AuthenticatorGetInfo::create( - $data['attestationTypes'] - ) : null; - $object->keyProtection = $data['keyProtection'] ?? []; - $object->isKeyRestricted = $data['isKeyRestricted'] ?? null; - $object->isFreshUserVerificationRequired = $data['isFreshUserVerificationRequired'] ?? null; - $object->cryptoStrength = $data['cryptoStrength'] ?? null; - $object->attachmentHint = $data['attachmentHint'] ?? []; - $object->tcDisplayContentType = $data['tcDisplayContentType'] ?? null; - if (isset($data['tcDisplayPNGCharacteristics'])) { - $tcDisplayPNGCharacteristics = $data['tcDisplayPNGCharacteristics']; - is_array($tcDisplayPNGCharacteristics) || throw MetadataStatementLoadingException::create( - 'Invalid Metadata Statement. The parameter "tcDisplayPNGCharacteristics" shall be a list of objects.' - ); - foreach ($tcDisplayPNGCharacteristics as $tcDisplayPNGCharacteristic) { - is_array($tcDisplayPNGCharacteristic) || throw MetadataStatementLoadingException::create( - 'Invalid Metadata Statement' - ); - $object->tcDisplayPNGCharacteristics[] = DisplayPNGCharacteristicsDescriptor::createFromArray( - $tcDisplayPNGCharacteristic - ); - } - } - $object->ecdaaTrustAnchors = $data['ecdaaTrustAnchors'] ?? []; - $object->icon = $data['icon'] ?? null; - if (isset($data['supportedExtensions'])) { - $supportedExtensions = $data['supportedExtensions']; - is_array($supportedExtensions) || throw MetadataStatementLoadingException::create( - 'Invalid Metadata Statement' - ); - foreach ($supportedExtensions as $supportedExtension) { - is_array($supportedExtension) || throw MetadataStatementLoadingException::create( - 'Invalid Metadata Statement' - ); - $object->supportedExtensions[] = ExtensionDescriptor::createFromArray($supportedExtension); - } - } - - return $object; } /** @@ -667,6 +703,6 @@ public function jsonSerialize(): array 'supportedExtensions' => $this->supportedExtensions, ]; - return Utils::filterNullValues($data); + return self::filterNullValues($data); } } diff --git a/src/metadata-service/src/Statement/PatternAccuracyDescriptor.php b/src/metadata-service/src/Statement/PatternAccuracyDescriptor.php index f486ae8b..9838e1db 100644 --- a/src/metadata-service/src/Statement/PatternAccuracyDescriptor.php +++ b/src/metadata-service/src/Statement/PatternAccuracyDescriptor.php @@ -5,12 +5,14 @@ namespace Webauthn\MetadataService\Statement; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; -use Webauthn\MetadataService\Utils; +use Webauthn\MetadataService\ValueFilter; use function array_key_exists; use function is_int; class PatternAccuracyDescriptor extends AbstractDescriptor { + use ValueFilter; + public function __construct( public readonly int $minComplexity, ?int $maxRetries = null, @@ -29,6 +31,7 @@ public static function create(int $minComplexity, ?int $maxRetries = null, ?int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getMinComplexity(): int { @@ -37,10 +40,12 @@ public function getMinComplexity(): int /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { - $data = Utils::filterNullValues($data); + $data = self::filterNullValues($data); array_key_exists('minComplexity', $data) || throw MetadataStatementLoadingException::create( 'The key "minComplexity" is missing' ); @@ -66,6 +71,6 @@ public function jsonSerialize(): array 'blockSlowdown' => $this->blockSlowdown, ]; - return Utils::filterNullValues($data); + return self::filterNullValues($data); } } diff --git a/src/metadata-service/src/Statement/RgbPaletteEntry.php b/src/metadata-service/src/Statement/RgbPaletteEntry.php index 98f43afa..a2bd1ea3 100644 --- a/src/metadata-service/src/Statement/RgbPaletteEntry.php +++ b/src/metadata-service/src/Statement/RgbPaletteEntry.php @@ -28,6 +28,7 @@ public static function create(int $r, int $g, int $b): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getR(): int { @@ -36,6 +37,7 @@ public function getR(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getG(): int { @@ -44,6 +46,7 @@ public function getG(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getB(): int { @@ -52,6 +55,8 @@ public function getB(): int /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { diff --git a/src/metadata-service/src/Statement/RogueListEntry.php b/src/metadata-service/src/Statement/RogueListEntry.php index 9f792a0a..bea96c6a 100644 --- a/src/metadata-service/src/Statement/RogueListEntry.php +++ b/src/metadata-service/src/Statement/RogueListEntry.php @@ -24,6 +24,7 @@ public static function create(string $sk, string $date): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getSk(): string { @@ -32,6 +33,7 @@ public function getSk(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getDate(): ?string { @@ -40,6 +42,8 @@ public function getDate(): ?string /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { diff --git a/src/metadata-service/src/Statement/StatusReport.php b/src/metadata-service/src/Statement/StatusReport.php index 337c1b6c..45b78f6a 100644 --- a/src/metadata-service/src/Statement/StatusReport.php +++ b/src/metadata-service/src/Statement/StatusReport.php @@ -6,13 +6,15 @@ use JsonSerializable; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; -use Webauthn\MetadataService\Utils; +use Webauthn\MetadataService\ValueFilter; use function array_key_exists; use function in_array; use function is_string; class StatusReport implements JsonSerializable { + use ValueFilter; + /** * @see AuthenticatorStatus */ @@ -65,6 +67,7 @@ public function isCompromised(): bool /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getStatus(): string { @@ -73,6 +76,7 @@ public function getStatus(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getEffectiveDate(): ?string { @@ -81,6 +85,7 @@ public function getEffectiveDate(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCertificate(): ?string { @@ -89,6 +94,7 @@ public function getCertificate(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getUrl(): ?string { @@ -97,6 +103,7 @@ public function getUrl(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCertificationDescriptor(): ?string { @@ -105,6 +112,7 @@ public function getCertificationDescriptor(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCertificateNumber(): ?string { @@ -113,6 +121,7 @@ public function getCertificateNumber(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCertificationPolicyVersion(): ?string { @@ -121,6 +130,7 @@ public function getCertificationPolicyVersion(): ?string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCertificationRequirementsVersion(): ?string { @@ -129,10 +139,12 @@ public function getCertificationRequirementsVersion(): ?string /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { - $data = Utils::filterNullValues($data); + $data = self::filterNullValues($data); array_key_exists('status', $data) || throw MetadataStatementLoadingException::create( 'The key "status" is missing' ); @@ -182,6 +194,6 @@ public function jsonSerialize(): array 'certificationRequirementsVersion' => $this->certificationRequirementsVersion, ]; - return Utils::filterNullValues($data); + return self::filterNullValues($data); } } diff --git a/src/metadata-service/src/Statement/VerificationMethodANDCombinations.php b/src/metadata-service/src/Statement/VerificationMethodANDCombinations.php index a7ff6f11..174dcc83 100644 --- a/src/metadata-service/src/Statement/VerificationMethodANDCombinations.php +++ b/src/metadata-service/src/Statement/VerificationMethodANDCombinations.php @@ -12,6 +12,7 @@ class VerificationMethodANDCombinations implements JsonSerializable * @param VerificationMethodDescriptor[] $verificationMethods */ public function __construct( + /** @readonly */ public array $verificationMethods = [] ) { } @@ -25,7 +26,8 @@ public static function create(array $verificationMethods): self } /** - * @deprecated since 4.7.0. Please use the property directly. + * @deprecated since 4.7.0. Please use the {self::create} directly. + * @infection-ignore-all */ public function addVerificationMethodDescriptor(VerificationMethodDescriptor $verificationMethodDescriptor): self { @@ -37,6 +39,7 @@ public function addVerificationMethodDescriptor(VerificationMethodDescriptor $ve /** * @return VerificationMethodDescriptor[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getVerificationMethods(): array { @@ -45,6 +48,8 @@ public function getVerificationMethods(): array /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { diff --git a/src/metadata-service/src/Statement/VerificationMethodDescriptor.php b/src/metadata-service/src/Statement/VerificationMethodDescriptor.php index 6d0b19b3..3fb2aeb9 100644 --- a/src/metadata-service/src/Statement/VerificationMethodDescriptor.php +++ b/src/metadata-service/src/Statement/VerificationMethodDescriptor.php @@ -6,12 +6,14 @@ use JsonSerializable; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; -use Webauthn\MetadataService\Utils; +use Webauthn\MetadataService\ValueFilter; use function array_key_exists; use function is_array; class VerificationMethodDescriptor implements JsonSerializable { + use ValueFilter; + final public const USER_VERIFY_PRESENCE_INTERNAL = 'presence_internal'; final public const USER_VERIFY_PRESENCE_INTERNAL_INT = 0x00000001; @@ -118,6 +120,7 @@ public static function create( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getUserVerificationMethod(): string { @@ -191,6 +194,7 @@ public function all(): bool /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCaDesc(): ?CodeAccuracyDescriptor { @@ -199,6 +203,7 @@ public function getCaDesc(): ?CodeAccuracyDescriptor /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getBaDesc(): ?BiometricAccuracyDescriptor { @@ -207,6 +212,7 @@ public function getBaDesc(): ?BiometricAccuracyDescriptor /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getPaDesc(): ?PatternAccuracyDescriptor { @@ -215,10 +221,12 @@ public function getPaDesc(): ?PatternAccuracyDescriptor /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { - $data = Utils::filterNullValues($data); + $data = self::filterNullValues($data); if (isset($data['userVerification']) && ! isset($data['userVerificationMethod'])) { $data['userVerificationMethod'] = $data['userVerification']; unset($data['userVerification']); @@ -254,6 +262,6 @@ public function jsonSerialize(): array 'paDesc' => $this->paDesc, ]; - return Utils::filterNullValues($data); + return self::filterNullValues($data); } } diff --git a/src/metadata-service/src/Statement/Version.php b/src/metadata-service/src/Statement/Version.php index 646d831c..8588e620 100644 --- a/src/metadata-service/src/Statement/Version.php +++ b/src/metadata-service/src/Statement/Version.php @@ -6,12 +6,14 @@ use JsonSerializable; use Webauthn\MetadataService\Exception\MetadataStatementLoadingException; -use Webauthn\MetadataService\Utils; +use Webauthn\MetadataService\ValueFilter; use function array_key_exists; use function is_int; class Version implements JsonSerializable { + use ValueFilter; + public function __construct( public readonly ?int $major, public readonly ?int $minor @@ -30,6 +32,7 @@ public static function create(?int $major, ?int $minor): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getMajor(): ?int { @@ -38,6 +41,7 @@ public function getMajor(): ?int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getMinor(): ?int { @@ -46,10 +50,12 @@ public function getMinor(): ?int /** * @param array $data + * @deprecated since 4.7.0. Please use the symfony/serializer for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { - $data = Utils::filterNullValues($data); + $data = self::filterNullValues($data); foreach (['major', 'minor'] as $key) { if (array_key_exists($key, $data)) { is_int($data[$key]) || throw MetadataStatementLoadingException::create( @@ -71,6 +77,6 @@ public function jsonSerialize(): array 'minor' => $this->minor, ]; - return Utils::filterNullValues($data); + return self::filterNullValues($data); } } diff --git a/src/metadata-service/src/Utils.php b/src/metadata-service/src/ValueFilter.php similarity index 75% rename from src/metadata-service/src/Utils.php rename to src/metadata-service/src/ValueFilter.php index abc332bf..23a9700c 100644 --- a/src/metadata-service/src/Utils.php +++ b/src/metadata-service/src/ValueFilter.php @@ -7,14 +7,14 @@ /** * @internal */ -abstract class Utils +trait ValueFilter { /** * @param array $data * * @return array */ - public static function filterNullValues(array $data): array + private static function filterNullValues(array $data): array { return array_filter($data, static fn ($var): bool => $var !== null); } diff --git a/src/symfony/src/Controller/AssertionControllerFactory.php b/src/symfony/src/Controller/AssertionControllerFactory.php index 0c1f7a7d..647a21d3 100644 --- a/src/symfony/src/Controller/AssertionControllerFactory.php +++ b/src/symfony/src/Controller/AssertionControllerFactory.php @@ -46,6 +46,7 @@ public function setLogger(LoggerInterface $logger): void /** * @deprecated since 4.5.0 and will be removed in 5.0.0. Please use createRequestController instead. + * @infection-ignore-all */ public function createAssertionRequestController( string $profile, @@ -83,6 +84,7 @@ public function createRequestController( /** * @param string[] $securedRelyingPartyIds * @deprecated since 4.5.0 and will be removed in 5.0.0. Please use createResponseController instead. + * @infection-ignore-all */ public function createAssertionResponseController( OptionsStorage $optionStorage, diff --git a/src/symfony/src/Controller/AttestationControllerFactory.php b/src/symfony/src/Controller/AttestationControllerFactory.php index 26f71ffd..22f22b9b 100644 --- a/src/symfony/src/Controller/AttestationControllerFactory.php +++ b/src/symfony/src/Controller/AttestationControllerFactory.php @@ -34,6 +34,7 @@ public function __construct( /** * @deprecated since 4.5.0 and will be removed in 5.0.0. Please use createResponseController instead. + * @infection-ignore-all */ public function createAttestationRequestController( UserEntityGuesser $userEntityGuesser, @@ -77,6 +78,7 @@ public function createRequestController( /** * @param string[] $securedRelyingPartyIds * @deprecated since 4.5.0 and will be removed in 5.0.0. Please use createResponseController instead. + * @infection-ignore-all */ public function createAttestationResponseController( OptionsStorage $optionStorage, diff --git a/src/symfony/src/CredentialOptionsBuilder/ProfileBasedCreationOptionsBuilder.php b/src/symfony/src/CredentialOptionsBuilder/ProfileBasedCreationOptionsBuilder.php index 765276ce..9d1dd498 100644 --- a/src/symfony/src/CredentialOptionsBuilder/ProfileBasedCreationOptionsBuilder.php +++ b/src/symfony/src/CredentialOptionsBuilder/ProfileBasedCreationOptionsBuilder.php @@ -9,6 +9,7 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; +use Webauthn\AuthenticationExtensions\AuthenticationExtension; use Webauthn\AuthenticationExtensions\AuthenticationExtensionsClientInputs; use Webauthn\AuthenticatorSelectionCriteria; use Webauthn\Bundle\Dto\PublicKeyCredentialCreationOptionsRequest; @@ -57,28 +58,35 @@ public function getFromRequest( $content = $request->getContent(); $excludedCredentials = $this->getCredentials($userEntity); - $creationOptionsRequest = $this->getServerPublicKeyCredentialCreationOptionsRequest($content); - $authenticatorSelectionData = $creationOptionsRequest->authenticatorSelection; + $optionsRequest = $this->getServerPublicKeyCredentialCreationOptionsRequest($content); + $authenticatorSelectionData = $optionsRequest->authenticatorSelection; $authenticatorSelection = null; if (is_array($authenticatorSelectionData)) { $authenticatorSelection = AuthenticatorSelectionCriteria::createFromArray($authenticatorSelectionData); - } elseif ($creationOptionsRequest->userVerification !== null || $creationOptionsRequest->residentKey !== null || $creationOptionsRequest->authenticatorAttachment !== null) { - $residentKey = $creationOptionsRequest->residentKey ?? null; - $requireResidentKey = $creationOptionsRequest->requireResidentKey !== null ? filter_var( - $creationOptionsRequest->requireResidentKey, + } elseif ($optionsRequest->userVerification !== null || $optionsRequest->residentKey !== null || $optionsRequest->authenticatorAttachment !== null) { + $residentKey = $optionsRequest->residentKey ?? null; + $requireResidentKey = $optionsRequest->requireResidentKey !== null ? filter_var( + $optionsRequest->requireResidentKey, FILTER_VALIDATE_BOOLEAN ) : null; $authenticatorSelection = AuthenticatorSelectionCriteria::create( - $creationOptionsRequest->authenticatorAttachment, - $creationOptionsRequest->userVerification ?? AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED, + $optionsRequest->authenticatorAttachment, + $optionsRequest->userVerification ?? AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED, $residentKey, $requireResidentKey ); } - $extensions = $creationOptionsRequest->extensions; - if (is_array($extensions)) { - $extensions = AuthenticationExtensionsClientInputs::createFromArray($extensions); + $extensions = null; + if (is_array($optionsRequest->extensions)) { + $extensions = AuthenticationExtensionsClientInputs::create(array_map( + static fn (string $name, mixed $data): AuthenticationExtension => AuthenticationExtension::create( + $name, + $data + ), + array_keys($optionsRequest->extensions), + $optionsRequest->extensions + )); } return $this->publicKeyCredentialCreationOptionsFactory->create( @@ -86,7 +94,7 @@ public function getFromRequest( $userEntity, $excludedCredentials, $authenticatorSelection, - $creationOptionsRequest->attestation, + $optionsRequest->attestation, $extensions ); } diff --git a/src/symfony/src/CredentialOptionsBuilder/ProfileBasedRequestOptionsBuilder.php b/src/symfony/src/CredentialOptionsBuilder/ProfileBasedRequestOptionsBuilder.php index 16cdee83..1dc0d45a 100644 --- a/src/symfony/src/CredentialOptionsBuilder/ProfileBasedRequestOptionsBuilder.php +++ b/src/symfony/src/CredentialOptionsBuilder/ProfileBasedRequestOptionsBuilder.php @@ -9,6 +9,7 @@ use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; +use Webauthn\AuthenticationExtensions\AuthenticationExtension; use Webauthn\AuthenticationExtensions\AuthenticationExtensionsClientInputs; use Webauthn\Bundle\Dto\ServerPublicKeyCredentialRequestOptionsRequest; use Webauthn\Bundle\Repository\PublicKeyCredentialSourceRepositoryInterface; @@ -20,6 +21,7 @@ use Webauthn\PublicKeyCredentialSourceRepository; use Webauthn\PublicKeyCredentialUserEntity; use function count; +use function is_array; final class ProfileBasedRequestOptionsBuilder implements PublicKeyCredentialRequestOptionsBuilder { @@ -54,18 +56,26 @@ public function getFromRequest( ) ? $request->getContentTypeFormat() : $request->getContentType(); $format === 'json' || throw new BadRequestHttpException('Only JSON content type allowed'); $content = $request->getContent(); - $creationOptionsRequest = $this->getServerPublicKeyCredentialRequestOptionsRequest($content); - $extensions = $creationOptionsRequest->extensions !== null ? AuthenticationExtensionsClientInputs::createFromArray( - $creationOptionsRequest->extensions - ) : null; - $userEntity = $creationOptionsRequest->username === null ? null : $this->userEntityRepository->findOneByUsername( - $creationOptionsRequest->username + $optionsRequest = $this->getServerPublicKeyCredentialRequestOptionsRequest($content); + $extensions = null; + if (is_array($optionsRequest->extensions)) { + $extensions = AuthenticationExtensionsClientInputs::create(array_map( + static fn (string $name, mixed $data): AuthenticationExtension => AuthenticationExtension::create( + $name, + $data + ), + array_keys($optionsRequest->extensions), + $optionsRequest->extensions + )); + } + $userEntity = $optionsRequest->username === null ? null : $this->userEntityRepository->findOneByUsername( + $optionsRequest->username ); $allowedCredentials = $userEntity === null ? [] : $this->getCredentials($userEntity); return $this->publicKeyCredentialRequestOptionsFactory->create( $this->profile, $allowedCredentials, - $creationOptionsRequest->userVerification, + $optionsRequest->userVerification, $extensions ); } diff --git a/src/symfony/src/DependencyInjection/Factory/Security/WebauthnFactory.php b/src/symfony/src/DependencyInjection/Factory/Security/WebauthnFactory.php index 154ca719..22c2f284 100644 --- a/src/symfony/src/DependencyInjection/Factory/Security/WebauthnFactory.php +++ b/src/symfony/src/DependencyInjection/Factory/Security/WebauthnFactory.php @@ -34,6 +34,7 @@ use Webauthn\Bundle\Security\Storage\SessionStorage; use Webauthn\Bundle\Service\PublicKeyCredentialCreationOptionsFactory; use Webauthn\Bundle\Service\PublicKeyCredentialRequestOptionsFactory; +use Webauthn\Denormalizer\WebauthnSerializerFactory; use function array_key_exists; use function assert; @@ -77,21 +78,25 @@ final class WebauthnFactory implements FirewallListenerFactoryInterface, Authent /** * @deprecated This constant is not used anymore and will be removed in 5.0 + * @infection-ignore-all */ public const REQUEST_RESULT_LISTENER_DEFINITION_ID = 'webauthn.security.authentication.request_result_listener'; /** * @deprecated This constant is not used anymore and will be removed in 5.0 + * @infection-ignore-all */ public const CREATION_RESULT_LISTENER_DEFINITION_ID = 'webauthn.security.authentication.creation_result_listener'; /** * @deprecated This constant is not used anymore and will be removed in 5.0 + * @infection-ignore-all */ public const SUCCESS_HANDLER_ID_PREFIX = 'security.authentication.success_handler.webauthn.'; /** * @deprecated This constant is not used anymore and will be removed in 5.0 + * @infection-ignore-all */ public const FAILURE_HANDLER_ID_PREFIX = 'security.authentication.failure_handler.webauthn.'; @@ -469,6 +474,7 @@ private function getAssertionOptionsBuilderId( new Reference(PublicKeyCredentialSourceRepositoryInterface::class), new Reference(PublicKeyCredentialRequestOptionsFactory::class), $config['profile'], + new Reference(WebauthnSerializerFactory::class), ]); $container->setDefinition($optionsBuilderId, $optionsBuilder); @@ -492,6 +498,7 @@ private function getAttestationOptionsBuilderId( new Reference(PublicKeyCredentialSourceRepositoryInterface::class), new Reference(PublicKeyCredentialCreationOptionsFactory::class), $config['profile'], + new Reference(WebauthnSerializerFactory::class), ]); $container->setDefinition($optionsBuilderId, $optionsBuilder); diff --git a/src/symfony/src/DependencyInjection/WebauthnExtension.php b/src/symfony/src/DependencyInjection/WebauthnExtension.php index 36a533af..13a91426 100644 --- a/src/symfony/src/DependencyInjection/WebauthnExtension.php +++ b/src/symfony/src/DependencyInjection/WebauthnExtension.php @@ -104,7 +104,7 @@ public function load(array $configs, ContainerBuilder $container): void $loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config/')); $this->loadAndroidSafetyNet($container, $loader, $config['android_safetynet']); $this->loadMetadataServices($container, $loader, $config['metadata']); - $this->loadControllersSupport($container, $loader, $config['controllers']); + $this->loadControllersSupport($container, $config['controllers']); $container->setParameter('webauthn.creation_profiles', $config['creation_profiles']); $container->setParameter('webauthn.request_profiles', $config['request_profiles']); @@ -137,6 +137,9 @@ public function prepend(ContainerBuilder $container): void return; } $config = current($configs); + if (! is_array($config)) { + return; + } if (! isset($config['dbal'])) { $config['dbal'] = []; } @@ -157,13 +160,12 @@ public function prepend(ContainerBuilder $container): void /** * @param mixed[] $config */ - private function loadControllersSupport(ContainerBuilder $container, FileLoader $loader, array $config): void + private function loadControllersSupport(ContainerBuilder $container, array $config): void { if ($config['enabled'] === false) { return; } - $loader->load('controller.php'); $this->loadCreationControllersSupport($container, $config['creation'] ?? []); $this->loadRequestControllersSupport($container, $config['request'] ?? []); } diff --git a/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorCollection.php b/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorCollection.php index 854727c1..70b796e8 100644 --- a/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorCollection.php +++ b/src/symfony/src/Doctrine/Type/PublicKeyCredentialDescriptorCollection.php @@ -54,6 +54,7 @@ public static function create(array $publicKeyCredentialDescriptors): self /** * @deprecated since 4.7.0. Please use the {self::create} instead. + * @infection-ignore-all */ public function add(PublicKeyCredentialDescriptor ...$publicKeyCredentialDescriptors): void { @@ -64,6 +65,7 @@ public function add(PublicKeyCredentialDescriptor ...$publicKeyCredentialDescrip /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function has(string $id): bool { @@ -72,6 +74,7 @@ public function has(string $id): bool /** * @deprecated since 4.7.0. No replacement. + * @infection-ignore-all */ public function remove(string $id): void { diff --git a/src/symfony/src/Dto/AdditionalPublicKeyCredentialCreationOptionsRequest.php b/src/symfony/src/Dto/AdditionalPublicKeyCredentialCreationOptionsRequest.php index ff14ceb5..90b0881c 100644 --- a/src/symfony/src/Dto/AdditionalPublicKeyCredentialCreationOptionsRequest.php +++ b/src/symfony/src/Dto/AdditionalPublicKeyCredentialCreationOptionsRequest.php @@ -6,6 +6,7 @@ /** * @deprecated since 4.2 and will be removed in 5.0. Please use Webauthn\Bundle\Dto\PublicKeyCredentialCreationOptionsRequest instead + * @infection-ignore-all */ class AdditionalPublicKeyCredentialCreationOptionsRequest extends PublicKeyCredentialCreationOptionsRequest { diff --git a/src/symfony/src/Dto/PublicKeyCredentialCreationOptionsRequest.php b/src/symfony/src/Dto/PublicKeyCredentialCreationOptionsRequest.php index bff34d07..b6f1a641 100644 --- a/src/symfony/src/Dto/PublicKeyCredentialCreationOptionsRequest.php +++ b/src/symfony/src/Dto/PublicKeyCredentialCreationOptionsRequest.php @@ -15,6 +15,7 @@ class PublicKeyCredentialCreationOptionsRequest * @var array|null * * @deprecated Use $userVerification, $residentKey and $authenticatorAttachment + * @infection-ignore-all */ public ?array $authenticatorSelection = null; diff --git a/src/symfony/src/Repository/CanRegisterUserEntity.php b/src/symfony/src/Repository/CanRegisterUserEntity.php index b1789307..e928edbc 100644 --- a/src/symfony/src/Repository/CanRegisterUserEntity.php +++ b/src/symfony/src/Repository/CanRegisterUserEntity.php @@ -10,6 +10,7 @@ interface CanRegisterUserEntity { /** * @deprecated since 4.7.0 and will be removed in 5.0.0. Please use Webauthn\Bundle\Repository\CanGenerateUserEntity::generateUserEntity() instead. + * @infection-ignore-all */ public function generateNextUserEntityId(): string; diff --git a/src/symfony/src/Repository/PublicKeyCredentialSourceRepository.php b/src/symfony/src/Repository/PublicKeyCredentialSourceRepository.php index 8b9840a2..4e31bec8 100644 --- a/src/symfony/src/Repository/PublicKeyCredentialSourceRepository.php +++ b/src/symfony/src/Repository/PublicKeyCredentialSourceRepository.php @@ -8,6 +8,7 @@ /** * @deprecated since 4.6.0, to be removed in 5.0.0. Use Webauthn\Bundle\Repository\DoctrineCredentialSourceRepository instead. + * @infection-ignore-all */ class PublicKeyCredentialSourceRepository extends DoctrineCredentialSourceRepository implements PublicKeyCredentialSourceRepositoryInterface { diff --git a/src/symfony/src/Repository/PublicKeyCredentialUserEntityRepository.php b/src/symfony/src/Repository/PublicKeyCredentialUserEntityRepository.php index a01c8ec5..bf214b90 100644 --- a/src/symfony/src/Repository/PublicKeyCredentialUserEntityRepository.php +++ b/src/symfony/src/Repository/PublicKeyCredentialUserEntityRepository.php @@ -6,6 +6,7 @@ /** * @deprecated since 4.6.0, to be removed in 5.0.0. Use {@link PublicKeyCredentialUserEntityRepositoryInterface} and {@link CanRegisterUserEntity} instead. + * @infection-ignore-all */ interface PublicKeyCredentialUserEntityRepository extends PublicKeyCredentialUserEntityRepositoryInterface, CanRegisterUserEntity { diff --git a/src/symfony/src/Resources/config/controller.php b/src/symfony/src/Resources/config/controller.php deleted file mode 100644 index 6debcbad..00000000 --- a/src/symfony/src/Resources/config/controller.php +++ /dev/null @@ -1,35 +0,0 @@ -services() - ->defaults() - ->private() - ->autoconfigure(); - - $container - ->set(AttestationControllerFactory::class) - ->args([ - service(SerializerInterface::class), - service(ValidatorInterface::class), - service(PublicKeyCredentialCreationOptionsFactory::class), - service(PublicKeyCredentialLoader::class), - service(AuthenticatorAttestationResponseValidator::class), - service(PublicKeyCredentialSourceRepositoryInterface::class), - ]); - $container->set(DefaultFailureHandler::class); - $container->set(DefaultSuccessHandler::class); -}; diff --git a/src/symfony/src/Resources/config/services.php b/src/symfony/src/Resources/config/services.php index dd79cd71..d4bf587c 100644 --- a/src/symfony/src/Resources/config/services.php +++ b/src/symfony/src/Resources/config/services.php @@ -22,9 +22,25 @@ use Webauthn\Bundle\Repository\DummyPublicKeyCredentialUserEntityRepository; use Webauthn\Bundle\Repository\PublicKeyCredentialUserEntityRepositoryInterface; use Webauthn\Bundle\Routing\Loader; +use Webauthn\Bundle\Service\DefaultFailureHandler; +use Webauthn\Bundle\Service\DefaultSuccessHandler; use Webauthn\Bundle\Service\PublicKeyCredentialCreationOptionsFactory; use Webauthn\Bundle\Service\PublicKeyCredentialRequestOptionsFactory; use Webauthn\Counter\ThrowExceptionIfInvalid; +use Webauthn\Denormalizer\AttestationStatementDenormalizer; +use Webauthn\Denormalizer\AuthenticationExtensionsClientInputsDenormalizer; +use Webauthn\Denormalizer\AuthenticatorAssertionResponseDenormalizer; +use Webauthn\Denormalizer\AuthenticatorAttestationResponseDenormalizer; +use Webauthn\Denormalizer\AuthenticatorDataDenormalizer; +use Webauthn\Denormalizer\AuthenticatorResponseDenormalizer; +use Webauthn\Denormalizer\CollectedClientDataDenormalizer; +use Webauthn\Denormalizer\PublicKeyCredentialDenormalizer; +use Webauthn\Denormalizer\PublicKeyCredentialOptionsDenormalizer; +use Webauthn\Denormalizer\PublicKeyCredentialSourceDenormalizer; +use Webauthn\Denormalizer\PublicKeyCredentialUserEntityDenormalizer; +use Webauthn\Denormalizer\WebauthnSerializerFactory; +use Webauthn\MetadataService\Denormalizer\ExtensionDescriptorDenormalizer; +use Webauthn\MetadataService\Denormalizer\MetadataStatementSerializerFactory; use Webauthn\PublicKeyCredentialLoader; use Webauthn\PublicKeyCredentialSourceRepository; use Webauthn\TokenBinding\IgnoreTokenBindingHandler; @@ -71,7 +87,7 @@ ->public(); $container ->set(PublicKeyCredentialLoader::class) - ->args([service(AttestationObjectLoader::class)]) + ->args([service(AttestationObjectLoader::class), service('webauthn-serializer')]) ->public(); $container ->set(PublicKeyCredentialCreationOptionsFactory::class) @@ -151,4 +167,36 @@ $container ->alias('webauthn.request_factory.default', RequestFactoryInterface::class); + + $container->set(ExtensionDescriptorDenormalizer::class); + $container->set(AttestationStatementDenormalizer::class) + ->args([service(AttestationStatementSupportManager::class)]) + ; + $container->set(AuthenticationExtensionsClientInputsDenormalizer::class); + $container->set(AuthenticatorAssertionResponseDenormalizer::class); + $container->set(AuthenticatorAttestationResponseDenormalizer::class); + $container->set(AuthenticatorDataDenormalizer::class); + $container->set(AuthenticatorResponseDenormalizer::class); + $container->set(CollectedClientDataDenormalizer::class); + $container->set(PublicKeyCredentialDenormalizer::class); + $container->set(PublicKeyCredentialOptionsDenormalizer::class); + $container->set(PublicKeyCredentialSourceDenormalizer::class); + $container->set(PublicKeyCredentialUserEntityDenormalizer::class); + $container->set(WebauthnSerializerFactory::class) + ->args([service(AttestationStatementSupportManager::class)]) + ; + $container->set(MetadataStatementSerializerFactory::class); + $container->set('webauthn-serializer') + ->class(SerializerInterface::class) + ->factory([service(WebauthnSerializerFactory::class), 'create']) + ->public() + ; + $container->set('mds-serializer') + ->class(SerializerInterface::class) + ->factory([service(MetadataStatementSerializerFactory::class), 'create']) + ->public() + ; + + $container->set(DefaultFailureHandler::class); + $container->set(DefaultSuccessHandler::class); }; diff --git a/src/symfony/src/Security/Authentication/Token/WebauthnToken.php b/src/symfony/src/Security/Authentication/Token/WebauthnToken.php index acfbb629..91c9499f 100644 --- a/src/symfony/src/Security/Authentication/Token/WebauthnToken.php +++ b/src/symfony/src/Security/Authentication/Token/WebauthnToken.php @@ -8,24 +8,22 @@ use Webauthn\AuthenticationExtensions\AuthenticationExtensionsClientOutputs; use Webauthn\Bundle\Security\Authorization\Voter\IsUserPresentVoter; use Webauthn\Bundle\Security\Authorization\Voter\IsUserVerifiedVoter; -use Webauthn\Exception\InvalidDataException; use Webauthn\PublicKeyCredentialDescriptor; use Webauthn\PublicKeyCredentialOptions; use Webauthn\PublicKeyCredentialUserEntity; -use const JSON_THROW_ON_ERROR; class WebauthnToken extends AbstractToken implements WebauthnTokenInterface { public function __construct( - private PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity, - private PublicKeyCredentialOptions $publicKeyCredentialOptions, - private PublicKeyCredentialDescriptor $publicKeyCredentialDescriptor, + private readonly PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity, + private readonly PublicKeyCredentialOptions $publicKeyCredentialOptions, + private readonly PublicKeyCredentialDescriptor $publicKeyCredentialDescriptor, private readonly bool $isUserPresent, private readonly bool $isUserVerified, private readonly int $reservedForFutureUse1, private readonly int $reservedForFutureUse2, private readonly int $signCount, - private ?AuthenticationExtensionsClientOutputs $extensions, + private readonly ?AuthenticationExtensionsClientOutputs $extensions, private readonly string $firewallName, array $roles = [], private readonly bool $isBackupEligible = false, @@ -40,10 +38,9 @@ public function __construct( public function __serialize(): array { return [ - json_encode($this->publicKeyCredentialUserEntity, JSON_THROW_ON_ERROR), - json_encode($this->publicKeyCredentialDescriptor, JSON_THROW_ON_ERROR), - $this->publicKeyCredentialOptions::class, - json_encode($this->publicKeyCredentialOptions, JSON_THROW_ON_ERROR), + $this->publicKeyCredentialUserEntity, + $this->publicKeyCredentialDescriptor, + $this->publicKeyCredentialOptions, $this->isUserPresent, $this->isUserVerified, $this->isBackupEligible, @@ -58,15 +55,14 @@ public function __serialize(): array } /** - * @param array $serialized + * @param array $data */ - public function __unserialize(array $serialized): void + public function __unserialize(array $data): void { [ - $publicKeyCredentialUserEntity, - $publicKeyCredentialDescriptor, - $publicKeyCredentialOptionsClass, - $publicKeyCredentialOptions, + $this->publicKeyCredentialUserEntity, + $this->publicKeyCredentialDescriptor, + $this->publicKeyCredentialOptions, $this->isUserPresent, $this->isUserVerified, $this->isBackupEligible, @@ -74,28 +70,11 @@ public function __unserialize(array $serialized): void $this->reservedForFutureUse1, $this->reservedForFutureUse2, $this->signCount, - $extensions, + $this->extensions, $this->firewallName, $parentData - ] = $serialized; - is_subclass_of( - $publicKeyCredentialOptionsClass, - PublicKeyCredentialOptions::class - ) || throw InvalidDataException::create($serialized, 'Invalid PublicKeyCredentialOptions class'); - $this->publicKeyCredentialUserEntity = PublicKeyCredentialUserEntity::createFromString( - $publicKeyCredentialUserEntity - ); - $this->publicKeyCredentialDescriptor = PublicKeyCredentialDescriptor::createFromString( - $publicKeyCredentialDescriptor - ); - $this->publicKeyCredentialOptions = $publicKeyCredentialOptionsClass::createFromString( - $publicKeyCredentialOptions - ); - - $this->extensions = null; - if ($extensions !== null) { - $this->extensions = AuthenticationExtensionsClientOutputs::createFromString($extensions); - } + ] = $data; + parent::__unserialize($parentData); } diff --git a/src/symfony/src/Security/Authentication/Token/WebauthnTokenInterface.php b/src/symfony/src/Security/Authentication/Token/WebauthnTokenInterface.php index 479df224..9e775849 100644 --- a/src/symfony/src/Security/Authentication/Token/WebauthnTokenInterface.php +++ b/src/symfony/src/Security/Authentication/Token/WebauthnTokenInterface.php @@ -12,6 +12,7 @@ /** * @deprecated since 4.7.0, use {@see WebauthnToken} instead + * @infection-ignore-all */ interface WebauthnTokenInterface extends TokenInterface { diff --git a/src/webauthn/src/AttestationStatement/AttestationObject.php b/src/webauthn/src/AttestationStatement/AttestationObject.php index 99fc5b19..a89cccac 100644 --- a/src/webauthn/src/AttestationStatement/AttestationObject.php +++ b/src/webauthn/src/AttestationStatement/AttestationObject.php @@ -28,6 +28,7 @@ public static function create( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getRawAttestationObject(): string { @@ -36,6 +37,7 @@ public function getRawAttestationObject(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAttStmt(): AttestationStatement { @@ -44,6 +46,7 @@ public function getAttStmt(): AttestationStatement /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function setAttStmt(AttestationStatement $attStmt): void { @@ -52,6 +55,7 @@ public function setAttStmt(AttestationStatement $attStmt): void /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAuthData(): AuthenticatorData { @@ -60,6 +64,7 @@ public function getAuthData(): AuthenticatorData /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getMetadataStatement(): ?MetadataStatement { @@ -68,6 +73,7 @@ public function getMetadataStatement(): ?MetadataStatement /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function setMetadataStatement(MetadataStatement $metadataStatement): self { diff --git a/src/webauthn/src/AttestationStatement/AttestationStatement.php b/src/webauthn/src/AttestationStatement/AttestationStatement.php index a2a6cedf..6f216783 100644 --- a/src/webauthn/src/AttestationStatement/AttestationStatement.php +++ b/src/webauthn/src/AttestationStatement/AttestationStatement.php @@ -22,6 +22,7 @@ class AttestationStatement implements JsonSerializable /** * @deprecated since 4.2.0 and will be removed in 5.0.0. The ECDAA Trust Anchor does no longer exist in Webauthn specification. + * @infection-ignore-all */ final public const TYPE_ECDAA = 'ecdaa'; @@ -79,6 +80,7 @@ public static function createAttCA(string $fmt, array $attStmt, TrustPath $trust * @param array $attStmt * * @deprecated since 4.2.0 and will be removed in 5.0.0. The ECDAA Trust Anchor does no longer exist in Webauthn specification. + * @infection-ignore-all */ public static function createEcdaa(string $fmt, array $attStmt, TrustPath $trustPath): self { @@ -95,6 +97,7 @@ public static function createAnonymizationCA(string $fmt, array $attStmt, TrustP /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getFmt(): string { @@ -104,6 +107,7 @@ public function getFmt(): string /** * @return mixed[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAttStmt(): array { @@ -127,6 +131,7 @@ public function get(string $key): mixed /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getTrustPath(): TrustPath { @@ -135,6 +140,7 @@ public function getTrustPath(): TrustPath /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getType(): string { @@ -143,6 +149,8 @@ public function getType(): string /** * @param mixed[] $data + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { diff --git a/src/webauthn/src/AttestationStatement/AttestationStatementSupportManager.php b/src/webauthn/src/AttestationStatement/AttestationStatementSupportManager.php index 0a80eb88..b336da02 100644 --- a/src/webauthn/src/AttestationStatement/AttestationStatementSupportManager.php +++ b/src/webauthn/src/AttestationStatement/AttestationStatementSupportManager.php @@ -21,7 +21,10 @@ public function __construct( } } - public static function create(): self + /** + * @param AttestationStatementSupport[] $attestationStatementSupports + */ + public static function create(array $attestationStatementSupports = []): self { return new self(); } diff --git a/src/webauthn/src/AttestedCredentialData.php b/src/webauthn/src/AttestedCredentialData.php index 51fcc744..2a1585ad 100644 --- a/src/webauthn/src/AttestedCredentialData.php +++ b/src/webauthn/src/AttestedCredentialData.php @@ -34,6 +34,7 @@ public static function create( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAaguid(): AbstractUid { @@ -42,6 +43,7 @@ public function getAaguid(): AbstractUid /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function setAaguid(AbstractUid $aaguid): void { @@ -50,6 +52,7 @@ public function setAaguid(AbstractUid $aaguid): void /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCredentialId(): string { @@ -58,6 +61,7 @@ public function getCredentialId(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCredentialPublicKey(): ?string { diff --git a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtension.php b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtension.php index 4ec3c74f..3f9ff6a2 100644 --- a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtension.php +++ b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtension.php @@ -21,6 +21,7 @@ public static function create(string $name, mixed $value): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function name(): string { @@ -29,6 +30,7 @@ public function name(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function value(): mixed { diff --git a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensionsClientInputs.php b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensionsClientInputs.php index 2fd3df1f..90f6e304 100644 --- a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensionsClientInputs.php +++ b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensionsClientInputs.php @@ -34,6 +34,9 @@ public function __construct(array $extensions = []) } } + /** + * @param AuthenticationExtension[] $extensions + */ public static function create(array $extensions = []): self { return new self($extensions); @@ -41,6 +44,7 @@ public static function create(array $extensions = []): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function add(AuthenticationExtension ...$extensions): self { @@ -53,6 +57,8 @@ public function add(AuthenticationExtension ...$extensions): self /** * @param array $json + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $json): self { diff --git a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensionsClientOutputs.php b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensionsClientOutputs.php index b5c4e597..1179afa2 100644 --- a/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensionsClientOutputs.php +++ b/src/webauthn/src/AuthenticationExtensions/AuthenticationExtensionsClientOutputs.php @@ -22,6 +22,7 @@ class AuthenticationExtensionsClientOutputs implements JsonSerializable, Countab { /** * @var AuthenticationExtension[] + * @readonly */ public array $extensions = []; @@ -45,6 +46,7 @@ public static function create(array $extensions = []): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function add(AuthenticationExtension ...$extensions): void { @@ -53,6 +55,10 @@ public function add(AuthenticationExtension ...$extensions): void } } + /** + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all + */ public static function createFromString(string $data): self { $data = json_decode($data, true, flags: JSON_THROW_ON_ERROR); @@ -62,6 +68,8 @@ public static function createFromString(string $data): self /** * @param array $json + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $json): self { diff --git a/src/webauthn/src/AuthenticationExtensions/ExtensionOutputError.php b/src/webauthn/src/AuthenticationExtensions/ExtensionOutputError.php index dc7f3bbe..9adcc706 100644 --- a/src/webauthn/src/AuthenticationExtensions/ExtensionOutputError.php +++ b/src/webauthn/src/AuthenticationExtensions/ExtensionOutputError.php @@ -20,6 +20,7 @@ public function __construct( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAuthenticationExtension(): AuthenticationExtension { diff --git a/src/webauthn/src/AuthenticatorAssertionResponse.php b/src/webauthn/src/AuthenticatorAssertionResponse.php index 7627cc67..79de3a57 100644 --- a/src/webauthn/src/AuthenticatorAssertionResponse.php +++ b/src/webauthn/src/AuthenticatorAssertionResponse.php @@ -29,6 +29,7 @@ public static function create( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAuthenticatorData(): AuthenticatorData { @@ -37,6 +38,7 @@ public function getAuthenticatorData(): AuthenticatorData /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getSignature(): string { @@ -45,6 +47,7 @@ public function getSignature(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getUserHandle(): ?string { diff --git a/src/webauthn/src/AuthenticatorAttestationResponse.php b/src/webauthn/src/AuthenticatorAttestationResponse.php index ae8728bb..875bc525 100644 --- a/src/webauthn/src/AuthenticatorAttestationResponse.php +++ b/src/webauthn/src/AuthenticatorAttestationResponse.php @@ -35,6 +35,7 @@ public static function create( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAttestationObject(): AttestationObject { @@ -43,6 +44,7 @@ public function getAttestationObject(): AttestationObject /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all * * @return string[] */ diff --git a/src/webauthn/src/AuthenticatorData.php b/src/webauthn/src/AuthenticatorData.php index e0af7b1a..eebf1e5f 100644 --- a/src/webauthn/src/AuthenticatorData.php +++ b/src/webauthn/src/AuthenticatorData.php @@ -55,6 +55,7 @@ public static function create( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAuthData(): string { @@ -63,6 +64,7 @@ public function getAuthData(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getRpIdHash(): string { @@ -111,6 +113,7 @@ public function getReservedForFutureUse2(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getSignCount(): int { @@ -119,6 +122,7 @@ public function getSignCount(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAttestedCredentialData(): ?AttestedCredentialData { @@ -127,6 +131,7 @@ public function getAttestedCredentialData(): ?AttestedCredentialData /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getExtensions(): ?AuthenticationExtensionsClientOutputs { diff --git a/src/webauthn/src/AuthenticatorResponse.php b/src/webauthn/src/AuthenticatorResponse.php index 9a38daa7..162c24db 100644 --- a/src/webauthn/src/AuthenticatorResponse.php +++ b/src/webauthn/src/AuthenticatorResponse.php @@ -16,6 +16,7 @@ public function __construct( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getClientDataJSON(): CollectedClientData { diff --git a/src/webauthn/src/AuthenticatorSelectionCriteria.php b/src/webauthn/src/AuthenticatorSelectionCriteria.php index 4ba4fe1f..7fbcb801 100644 --- a/src/webauthn/src/AuthenticatorSelectionCriteria.php +++ b/src/webauthn/src/AuthenticatorSelectionCriteria.php @@ -42,6 +42,7 @@ class AuthenticatorSelectionCriteria implements JsonSerializable /** * @deprecated Please use AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_NO_PREFERENCE instead + * @infection-ignore-all */ final public const RESIDENT_KEY_REQUIREMENT_NONE = null; @@ -93,6 +94,7 @@ public static function create( /** * @deprecated since 4.7.0. Please use the {self::create} instead. + * @infection-ignore-all */ public function setAuthenticatorAttachment(?string $authenticatorAttachment): self { @@ -103,6 +105,7 @@ public function setAuthenticatorAttachment(?string $authenticatorAttachment): se /** * @deprecated since v4.1. Please use the {self::create} instead. + * @infection-ignore-all */ public function setRequireResidentKey(bool $requireResidentKey): self { @@ -116,6 +119,7 @@ public function setRequireResidentKey(bool $requireResidentKey): self /** * @deprecated since 4.7.0. Please use the {self::create} instead. + * @infection-ignore-all */ public function setUserVerification(string $userVerification): self { @@ -126,6 +130,7 @@ public function setUserVerification(string $userVerification): self /** * @deprecated since 4.7.0. Please use the {self::create} instead. + * @infection-ignore-all */ public function setResidentKey(null|string $residentKey): self { @@ -137,6 +142,7 @@ public function setResidentKey(null|string $residentKey): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAuthenticatorAttachment(): ?string { @@ -145,6 +151,7 @@ public function getAuthenticatorAttachment(): ?string /** * @deprecated Will be removed in 5.0. Please use the property directly. + * @infection-ignore-all */ public function isRequireResidentKey(): bool { @@ -153,6 +160,7 @@ public function isRequireResidentKey(): bool /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getUserVerification(): string { @@ -161,12 +169,17 @@ public function getUserVerification(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getResidentKey(): null|string { return $this->residentKey; } + /** + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all + */ public static function createFromString(string $data): self { $data = json_decode($data, true, flags: JSON_THROW_ON_ERROR); @@ -176,6 +189,8 @@ public static function createFromString(string $data): self /** * @param mixed[] $json + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $json): self { diff --git a/src/webauthn/src/CertificateChainChecker/CertificateChainChecker.php b/src/webauthn/src/CertificateChainChecker/CertificateChainChecker.php index fbd61481..60837ccd 100644 --- a/src/webauthn/src/CertificateChainChecker/CertificateChainChecker.php +++ b/src/webauthn/src/CertificateChainChecker/CertificateChainChecker.php @@ -8,6 +8,7 @@ /** * @deprecated since v4.1. Please use Webauthn\MetadataService\CertificateChainChecker\CertificateChainValidator instead + * @infection-ignore-all */ interface CertificateChainChecker extends CertificateChainValidator { diff --git a/src/webauthn/src/CertificateChainChecker/PhpCertificateChainChecker.php b/src/webauthn/src/CertificateChainChecker/PhpCertificateChainChecker.php index d483e362..17e791e4 100644 --- a/src/webauthn/src/CertificateChainChecker/PhpCertificateChainChecker.php +++ b/src/webauthn/src/CertificateChainChecker/PhpCertificateChainChecker.php @@ -8,6 +8,7 @@ /** * @deprecated since v4.1. Please use Webauthn\MetadataService\CertificateChainChecker\PhpCertificateChainValidator instead + * @infection-ignore-all */ final class PhpCertificateChainChecker extends PhpCertificateChainValidator { diff --git a/src/webauthn/src/CertificateToolbox.php b/src/webauthn/src/CertificateToolbox.php index 058d35f9..18b07aa9 100644 --- a/src/webauthn/src/CertificateToolbox.php +++ b/src/webauthn/src/CertificateToolbox.php @@ -8,6 +8,7 @@ /** * @deprecated since v4.1. Please use Webauthn\MetadataService\CertificateChainChecker\PhpCertificateChainValidator instead + * @infection-ignore-all */ class CertificateToolbox extends BaseCertificateToolbox { diff --git a/src/webauthn/src/CollectedClientData.php b/src/webauthn/src/CollectedClientData.php index d4ada859..f82b7cdd 100644 --- a/src/webauthn/src/CollectedClientData.php +++ b/src/webauthn/src/CollectedClientData.php @@ -30,6 +30,7 @@ class CollectedClientData /** * @var mixed[]|null * @deprecated Since 4.3.0 and will be removed in 5.0.0 + * @infection-ignore-all */ public readonly ?array $tokenBinding; @@ -97,6 +98,7 @@ public static function createFormJson(string $data): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getType(): string { @@ -105,6 +107,7 @@ public function getType(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getChallenge(): string { @@ -113,6 +116,7 @@ public function getChallenge(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getOrigin(): string { @@ -121,6 +125,7 @@ public function getOrigin(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCrossOrigin(): bool { @@ -129,6 +134,7 @@ public function getCrossOrigin(): bool /** * @deprecated Since 4.3.0 and will be removed in 5.0.0 + * @infection-ignore-all */ public function getTokenBinding(): ?TokenBinding { @@ -137,6 +143,7 @@ public function getTokenBinding(): ?TokenBinding /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getRawData(): string { diff --git a/src/webauthn/src/Credential.php b/src/webauthn/src/Credential.php index a8f243d8..cbad84cc 100644 --- a/src/webauthn/src/Credential.php +++ b/src/webauthn/src/Credential.php @@ -17,6 +17,7 @@ public function __construct( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getId(): string { @@ -25,6 +26,7 @@ public function getId(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getType(): string { diff --git a/src/webauthn/src/Denormalizer/AttestationObjectDenormalizer.php b/src/webauthn/src/Denormalizer/AttestationObjectDenormalizer.php new file mode 100644 index 00000000..80f7e1ac --- /dev/null +++ b/src/webauthn/src/Denormalizer/AttestationObjectDenormalizer.php @@ -0,0 +1,70 @@ +denormalizer === null) { + throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); + } + $stream = new StringStream($data); + $parsed = Decoder::create()->decode($stream); + + $parsed instanceof Normalizable || throw InvalidDataException::create( + $parsed, + 'Invalid attestation object. Unexpected object.' + ); + $attestationObject = $parsed->normalize(); + $stream->isEOF() || throw InvalidDataException::create( + null, + 'Invalid attestation object. Presence of extra bytes.' + ); + $stream->close(); + + $data = [ + 'rawAttestationObject' => $data, + 'attStmt' => $attestationObject, + 'authData' => $attestationObject['authData'], + ]; + $context[self::ALREADY_CALLED] = true; + + return $this->denormalizer->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + { + if ($context[self::ALREADY_CALLED] ?? false) { + return false; + } + + return $type === AttestationObject::class; + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + return [ + AttestationObject::class => false, + ]; + } +} diff --git a/src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php b/src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php new file mode 100644 index 00000000..15aaabae --- /dev/null +++ b/src/webauthn/src/Denormalizer/AttestationStatementDenormalizer.php @@ -0,0 +1,39 @@ +attestationStatementSupportManager->get($data['fmt']); + + return $attestationStatementSupport->load($data); + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + { + return $type === AttestationStatement::class; + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + return [ + AttestationStatement::class => false, + ]; + } +} diff --git a/src/webauthn/src/Denormalizer/AuthenticationExtensionsClientInputsDenormalizer.php b/src/webauthn/src/Denormalizer/AuthenticationExtensionsClientInputsDenormalizer.php new file mode 100644 index 00000000..005911e1 --- /dev/null +++ b/src/webauthn/src/Denormalizer/AuthenticationExtensionsClientInputsDenormalizer.php @@ -0,0 +1,48 @@ +denormalizer === null) { + throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); + } + + $context[self::ALREADY_CALLED] = true; + + return $this->denormalizer->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + { + if ($context[self::ALREADY_CALLED] ?? false) { + return false; + } + + return $type === AuthenticationExtensionsClientInputs::class; + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + return [ + AuthenticationExtensionsClientInputs::class => false, + ]; + } +} diff --git a/src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php b/src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php new file mode 100644 index 00000000..027d2a07 --- /dev/null +++ b/src/webauthn/src/Denormalizer/AuthenticatorAssertionResponseDenormalizer.php @@ -0,0 +1,57 @@ +denormalizer === null) { + throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); + } + + $data['authenticatorData'] = Base64::decode($data['authenticatorData']); + $data['signature'] = Base64::decode($data['signature']); + $data['clientDataJSON'] = Base64UrlSafe::decodeNoPadding($data['clientDataJSON']); + $userHandle = $data['userHandle'] ?? null; + if ($userHandle !== '' && $userHandle !== null) { + $data['userHandle'] = Base64::decode($userHandle); + } + + $context[self::ALREADY_CALLED] = true; + return $this->denormalizer->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + { + if ($context[self::ALREADY_CALLED] ?? false) { + return false; + } + + return $type === AuthenticatorAssertionResponse::class; + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + return [ + AuthenticatorAssertionResponse::class => false, + ]; + } +} diff --git a/src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php b/src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php new file mode 100644 index 00000000..fb82fca0 --- /dev/null +++ b/src/webauthn/src/Denormalizer/AuthenticatorAttestationResponseDenormalizer.php @@ -0,0 +1,53 @@ +denormalizer === null) { + throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); + } + + $data['clientDataJSON'] = Base64UrlSafe::decodeNoPadding($data['clientDataJSON']); + $data['attestationObject'] = Base64::decode($data['attestationObject']); + + $context[self::ALREADY_CALLED] = true; + + return $this->denormalizer->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + { + if ($context[self::ALREADY_CALLED] ?? false) { + return false; + } + + return $type === AuthenticatorAttestationResponse::class; + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + return [ + AuthenticatorAttestationResponse::class => false, + ]; + } +} diff --git a/src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php b/src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php new file mode 100644 index 00000000..f7c18ff3 --- /dev/null +++ b/src/webauthn/src/Denormalizer/AuthenticatorDataDenormalizer.php @@ -0,0 +1,147 @@ +decoder = Decoder::create(); + } + + public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + { + if ($this->denormalizer === null) { + throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); + } + + $authData = $this->fixIncorrectEdDSAKey($data); + $authDataStream = new StringStream($authData); + $rp_id_hash = $authDataStream->read(32); + $flags = $authDataStream->read(1); + $signCount = $authDataStream->read(4); + $signCount = unpack('N', $signCount); + + $attestedCredentialData = null; + if (0 !== (ord($flags) & AuthenticatorData::FLAG_AT)) { + $aaguid = Uuid::fromBinary($authDataStream->read(16)); + $credentialLength = $authDataStream->read(2); + $credentialLength = unpack('n', $credentialLength); + $credentialId = $authDataStream->read($credentialLength[1]); + $credentialPublicKey = $this->decoder->decode($authDataStream); + $credentialPublicKey instanceof MapObject || throw InvalidDataException::create( + $authData, + 'The data does not contain a valid credential public key.' + ); + $attestedCredentialData = new AttestedCredentialData( + $aaguid, + $credentialId, + (string) $credentialPublicKey + ); + } + $extension = null; + if (0 !== (ord($flags) & AuthenticatorData::FLAG_ED)) { + $extension = $this->decoder->decode($authDataStream); + $extension = AuthenticationExtensionsClientOutputsLoader::load($extension); + } + $authDataStream->isEOF() || throw InvalidDataException::create( + $authData, + 'Invalid authentication data. Presence of extra bytes.' + ); + $authDataStream->close(); + $data = [ + 'authData' => $authData, + 'rpIdHash' => $rp_id_hash, + 'flags' => $flags, + 'signCount' => $signCount[1], + 'attestedCredentialData' => $attestedCredentialData, + 'extensions' => $extension, + ]; + $context[self::ALREADY_CALLED] = true; + + return $this->denormalizer->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + { + if ($context[self::ALREADY_CALLED] ?? false) { + return false; + } + + return $type === AuthenticatorData::class; + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + return [ + AuthenticatorData::class => false, + ]; + } + + private function fixIncorrectEdDSAKey(string $data): string + { + $needle = hex2bin('a301634f4b500327206745643235353139'); + $correct = hex2bin('a401634f4b500327206745643235353139'); + $position = mb_strpos($data, $needle, 0, '8bit'); + if ($position === false) { + return $data; + } + + $begin = mb_substr($data, 0, $position, '8bit'); + $end = mb_substr($data, $position, null, '8bit'); + $end = str_replace($needle, $correct, $end); + $cbor = new StringStream($end); + $badKey = $this->decoder->decode($cbor); + + ($badKey instanceof MapObject && $cbor->isEOF()) || throw InvalidDataException::create( + $end, + 'Invalid authentication data. Presence of extra bytes.' + ); + $badX = $badKey->get(-2); + $badX instanceof ListObject || throw InvalidDataException::create($end, 'Invalid authentication data.'); + $keyBytes = array_reduce( + $badX->normalize(), + static fn (string $carry, string $item): string => $carry . chr((int) $item), + '' + ); + $correctX = ByteStringObject::create($keyBytes); + $correctKey = MapObject::create() + ->add(UnsignedIntegerObject::create(1), TextStringObject::create('OKP')) + ->add(UnsignedIntegerObject::create(3), NegativeIntegerObject::create(-8)) + ->add(NegativeIntegerObject::create(-1), TextStringObject::create('Ed25519')) + ->add(NegativeIntegerObject::create(-2), $correctX); + + return $begin . $correctKey; + } +} diff --git a/src/webauthn/src/Denormalizer/AuthenticatorResponseDenormalizer.php b/src/webauthn/src/Denormalizer/AuthenticatorResponseDenormalizer.php new file mode 100644 index 00000000..35d2e43f --- /dev/null +++ b/src/webauthn/src/Denormalizer/AuthenticatorResponseDenormalizer.php @@ -0,0 +1,69 @@ +denormalizer === null) { + throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); + } + + switch (true) { + case array_key_exists('attestationObject', $data): + $context[self::ALREADY_CALLED] = true; + return $this->denormalizer->denormalize( + $data, + AuthenticatorAttestationResponse::class, + $format, + $context + ); + case array_key_exists('authenticatorData', $data) && array_key_exists('signature', $data): + $context[self::ALREADY_CALLED] = true; + return $this->denormalizer->denormalize( + $data, + AuthenticatorAssertionResponse::class, + $format, + $context + ); + default: + throw InvalidDataException::create($data, 'Unable to create the response object'); + } + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + { + if ($context[self::ALREADY_CALLED] ?? false) { + return false; + } + + return $type === AuthenticatorResponse::class; + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + return [ + AuthenticatorResponse::class => false, + ]; + } +} diff --git a/src/webauthn/src/Denormalizer/CollectedClientDataDenormalizer.php b/src/webauthn/src/Denormalizer/CollectedClientDataDenormalizer.php new file mode 100644 index 00000000..84b8dee1 --- /dev/null +++ b/src/webauthn/src/Denormalizer/CollectedClientDataDenormalizer.php @@ -0,0 +1,52 @@ +denormalizer === null) { + throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); + } + $data = [ + 'data' => json_decode($data, true, flags: JSON_THROW_ON_ERROR), + 'rawData' => $data, + ]; + $context[self::ALREADY_CALLED] = true; + + return $this->denormalizer->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + { + if ($context[self::ALREADY_CALLED] ?? false) { + return false; + } + + return $type === CollectedClientData::class; + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + return [ + CollectedClientData::class => false, + ]; + } +} diff --git a/src/webauthn/src/Denormalizer/PublicKeyCredentialDenormalizer.php b/src/webauthn/src/Denormalizer/PublicKeyCredentialDenormalizer.php new file mode 100644 index 00000000..b3f206a9 --- /dev/null +++ b/src/webauthn/src/Denormalizer/PublicKeyCredentialDenormalizer.php @@ -0,0 +1,59 @@ +denormalizer === null) { + throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); + } + if (! array_key_exists('id', $data)) { + return $data; + } + $id = Base64UrlSafe::decodeNoPadding($data['id']); + $rawId = Base64::decode($data['rawId']); + hash_equals($id, $rawId) || throw InvalidDataException::create($data, 'Invalid ID'); + $data['rawId'] = $rawId; + + $context[self::ALREADY_CALLED] = true; + + return $this->denormalizer->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + { + if ($context[self::ALREADY_CALLED] ?? false) { + return false; + } + + return $type === PublicKeyCredential::class; + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + return [ + PublicKeyCredential::class => false, + ]; + } +} diff --git a/src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php b/src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php new file mode 100644 index 00000000..77da3291 --- /dev/null +++ b/src/webauthn/src/Denormalizer/PublicKeyCredentialOptionsDenormalizer.php @@ -0,0 +1,68 @@ +denormalizer === null) { + throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); + } + if (! array_key_exists('challenge', $data)) { + return $data; + } + + $data['challenge'] = Base64UrlSafe::decodeNoPadding($data['challenge']); + foreach (['allowCredentials', 'excludeCredentials'] as $key) { + if (array_key_exists('allowCredentials', $data)) { + foreach ($data[$key] ?? [] as $item => $allowCredential) { + $data[$key][$item]['id'] = Base64UrlSafe::decodeNoPadding($allowCredential['id']); + } + } + } + $context[self::ALREADY_CALLED] = true; + + return $this->denormalizer->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + { + if ($context[self::ALREADY_CALLED] ?? false) { + return false; + } + + return in_array( + $type, + [PublicKeyCredentialCreationOptions::class, PublicKeyCredentialRequestOptions::class], + true + ); + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + return [ + PublicKeyCredentialCreationOptions::class => false, + PublicKeyCredentialRequestOptions::class => false, + ]; + } +} diff --git a/src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php b/src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php new file mode 100644 index 00000000..3a643bfb --- /dev/null +++ b/src/webauthn/src/Denormalizer/PublicKeyCredentialSourceDenormalizer.php @@ -0,0 +1,56 @@ +denormalizer === null) { + throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); + } + $keys = ['publicKeyCredentialId', 'credentialPublicKey', 'userHandle']; + foreach ($keys as $key) { + if (! array_key_exists($key, $data)) { + return $data; + } + $data[$key] = Base64::decode($data[$key]); + } + $context[self::ALREADY_CALLED] = true; + + return $this->denormalizer->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + { + if ($context[self::ALREADY_CALLED] ?? false) { + return false; + } + + return $type === PublicKeyCredentialSource::class; + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + return [ + PublicKeyCredentialSource::class => false, + ]; + } +} diff --git a/src/webauthn/src/Denormalizer/PublicKeyCredentialUserEntityDenormalizer.php b/src/webauthn/src/Denormalizer/PublicKeyCredentialUserEntityDenormalizer.php new file mode 100644 index 00000000..f6f3e477 --- /dev/null +++ b/src/webauthn/src/Denormalizer/PublicKeyCredentialUserEntityDenormalizer.php @@ -0,0 +1,53 @@ +denormalizer === null) { + throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); + } + if (! array_key_exists('id', $data)) { + return $data; + } + $data['id'] = Base64::decode($data['id']); + $context[self::ALREADY_CALLED] = true; + + return $this->denormalizer->denormalize($data, $type, $format, $context); + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + { + if ($context[self::ALREADY_CALLED] ?? false) { + return false; + } + + return $type === PublicKeyCredentialUserEntity::class; + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + return [ + PublicKeyCredentialUserEntity::class => false, + ]; + } +} diff --git a/src/webauthn/src/Denormalizer/WebauthnSerializerFactory.php b/src/webauthn/src/Denormalizer/WebauthnSerializerFactory.php new file mode 100644 index 00000000..a02a3166 --- /dev/null +++ b/src/webauthn/src/Denormalizer/WebauthnSerializerFactory.php @@ -0,0 +1,80 @@ + $package) { + if (! class_exists($class)) { + throw new RuntimeException(sprintf( + 'The class "%s" is required. Please install the package "%s" to use this feature.', + $class, + $package + )); + } + } + + $denormalizers = [ + new AttestationObjectDenormalizer(), + new AttestationStatementDenormalizer($this->attestationStatementSupportManager), + new AuthenticationExtensionsClientInputsDenormalizer(), + new AuthenticatorAssertionResponseDenormalizer(), + new AuthenticatorAttestationResponseDenormalizer(), + new AuthenticatorDataDenormalizer(), + new AuthenticatorResponseDenormalizer(), + new CollectedClientDataDenormalizer(), + new PublicKeyCredentialDenormalizer(), + new PublicKeyCredentialOptionsDenormalizer(), + new PublicKeyCredentialSourceDenormalizer(), + new PublicKeyCredentialUserEntityDenormalizer(), + new UidNormalizer(), + new ArrayDenormalizer(), + new ObjectNormalizer( + propertyTypeExtractor: new PropertyInfoExtractor(typeExtractors: [ + new PhpDocExtractor(), + new ReflectionExtractor(), + ]) + ), + ]; + + return new Serializer($denormalizers, [new JsonEncoder()]); + } + + /** + * @return array + */ + private static function getRequiredSerializerClasses(): array + { + return [ + UidNormalizer::class => 'symfony/serializer', + ArrayDenormalizer::class => 'symfony/serializer', + ObjectNormalizer::class => 'symfony/serializer', + PropertyInfoExtractor::class => 'symfony/serializer', + PhpDocExtractor::class => 'phpdocumentor/reflection-docblock', + ReflectionExtractor::class => 'symfony/serializer', + JsonEncoder::class => 'symfony/serializer', + Serializer::class => 'symfony/serializer', + ]; + } +} diff --git a/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationFailedEvent.php b/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationFailedEvent.php index f8caa082..140322ca 100644 --- a/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationFailedEvent.php +++ b/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationFailedEvent.php @@ -42,6 +42,7 @@ public function __construct( /** * @deprecated since 4.7.0 and will be removed in 5.0.0. Please use the `getCredential()` method instead + * @infection-ignore-all */ public function getCredentialId(): string { @@ -65,6 +66,7 @@ public function getPublicKeyCredentialRequestOptions(): PublicKeyCredentialReque /** * @deprecated since 4.5.0 and will be removed in 5.0.0. Please use the `host` property instead + * @infection-ignore-all */ public function getRequest(): ServerRequestInterface|string { diff --git a/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationSucceededEvent.php b/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationSucceededEvent.php index bc4fa617..347e6fa2 100644 --- a/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationSucceededEvent.php +++ b/src/webauthn/src/Event/AuthenticatorAssertionResponseValidationSucceededEvent.php @@ -56,6 +56,7 @@ public function getPublicKeyCredentialRequestOptions(): PublicKeyCredentialReque /** * @deprecated since 4.5.0 and will be removed in 5.0.0. Please use the `host` property instead + * @infection-ignore-all */ public function getRequest(): ServerRequestInterface|string { diff --git a/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationFailedEvent.php b/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationFailedEvent.php index 38230634..d7edd0f4 100644 --- a/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationFailedEvent.php +++ b/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationFailedEvent.php @@ -42,6 +42,7 @@ public function getPublicKeyCredentialCreationOptions(): PublicKeyCredentialCrea /** * @deprecated since 4.5.0 and will be removed in 5.0.0. Please use the `host` property instead + * @infection-ignore-all */ public function getRequest(): ServerRequestInterface|string { diff --git a/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationSucceededEvent.php b/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationSucceededEvent.php index a4f113e9..aae7af83 100644 --- a/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationSucceededEvent.php +++ b/src/webauthn/src/Event/AuthenticatorAttestationResponseValidationSucceededEvent.php @@ -42,6 +42,7 @@ public function getPublicKeyCredentialCreationOptions(): PublicKeyCredentialCrea /** * @deprecated since 4.5.0 and will be removed in 5.0.0. Please use the `host` property instead + * @infection-ignore-all */ public function getRequest(): ServerRequestInterface|string { diff --git a/src/webauthn/src/PublicKeyCredential.php b/src/webauthn/src/PublicKeyCredential.php index 806a96c2..b4cbd48f 100644 --- a/src/webauthn/src/PublicKeyCredential.php +++ b/src/webauthn/src/PublicKeyCredential.php @@ -24,6 +24,7 @@ public function __construct( /** * @deprecated since 4.8.0. Please use the PublicKeyCredentialDescriptor ({self::getPublicKeyCredentialDescriptor}) instead. + * @infection-ignore-all */ public function __toString(): string { @@ -37,6 +38,7 @@ public static function create(string $id, string $type, string $rawId, Authentic /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getRawId(): string { @@ -45,6 +47,7 @@ public function getRawId(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getResponse(): AuthenticatorResponse { diff --git a/src/webauthn/src/PublicKeyCredentialCreationOptions.php b/src/webauthn/src/PublicKeyCredentialCreationOptions.php index 6e92b3df..7c9c4a94 100644 --- a/src/webauthn/src/PublicKeyCredentialCreationOptions.php +++ b/src/webauthn/src/PublicKeyCredentialCreationOptions.php @@ -109,6 +109,7 @@ public static function create( /** * @deprecated since 4.7.0. Please use the {self::create} instead. + * @infection-ignore-all */ public function addPubKeyCredParam(PublicKeyCredentialParameters $pubKeyCredParam): self { @@ -119,6 +120,7 @@ public function addPubKeyCredParam(PublicKeyCredentialParameters $pubKeyCredPara /** * @deprecated since 4.7.0. No replacement. Please use the {self::create} instead. + * @infection-ignore-all */ public function addPubKeyCredParams(PublicKeyCredentialParameters ...$pubKeyCredParams): self { @@ -131,6 +133,7 @@ public function addPubKeyCredParams(PublicKeyCredentialParameters ...$pubKeyCred /** * @deprecated since 4.7.0. Please use the {self::create} instead. + * @infection-ignore-all */ public function excludeCredential(PublicKeyCredentialDescriptor $excludeCredential): self { @@ -141,6 +144,7 @@ public function excludeCredential(PublicKeyCredentialDescriptor $excludeCredenti /** * @deprecated since 4.7.0. No replacement. Please use the {self::create} instead. + * @infection-ignore-all */ public function excludeCredentials(PublicKeyCredentialDescriptor ...$excludeCredentials): self { @@ -153,6 +157,7 @@ public function excludeCredentials(PublicKeyCredentialDescriptor ...$excludeCred /** * @deprecated since 4.7.0. Please use the {self::create} instead. + * @infection-ignore-all */ public function setAuthenticatorSelection(?AuthenticatorSelectionCriteria $authenticatorSelection): self { @@ -163,6 +168,7 @@ public function setAuthenticatorSelection(?AuthenticatorSelectionCriteria $authe /** * @deprecated since 4.7.0. Please use the {self::create} instead. + * @infection-ignore-all */ public function setAttestation(string $attestation): self { @@ -177,6 +183,7 @@ public function setAttestation(string $attestation): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getRp(): PublicKeyCredentialRpEntity { @@ -185,6 +192,7 @@ public function getRp(): PublicKeyCredentialRpEntity /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getUser(): PublicKeyCredentialUserEntity { @@ -194,6 +202,7 @@ public function getUser(): PublicKeyCredentialUserEntity /** * @return PublicKeyCredentialParameters[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getPubKeyCredParams(): array { @@ -203,6 +212,7 @@ public function getPubKeyCredParams(): array /** * @return PublicKeyCredentialDescriptor[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getExcludeCredentials(): array { @@ -211,6 +221,7 @@ public function getExcludeCredentials(): array /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAuthenticatorSelection(): ?AuthenticatorSelectionCriteria { @@ -219,12 +230,17 @@ public function getAuthenticatorSelection(): ?AuthenticatorSelectionCriteria /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAttestation(): ?string { return $this->attestation; } + /** + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all + */ public static function createFromString(string $data): static { $data = json_decode($data, true, flags: JSON_THROW_ON_ERROR); @@ -232,6 +248,10 @@ public static function createFromString(string $data): static return self::createFromArray($data); } + /** + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all + */ public static function createFromArray(array $json): static { array_key_exists('rp', $json) || throw InvalidDataException::create($json, 'Invalid input. "rp" is missing.'); diff --git a/src/webauthn/src/PublicKeyCredentialDescriptor.php b/src/webauthn/src/PublicKeyCredentialDescriptor.php index 097403ec..ba38446e 100644 --- a/src/webauthn/src/PublicKeyCredentialDescriptor.php +++ b/src/webauthn/src/PublicKeyCredentialDescriptor.php @@ -53,6 +53,7 @@ public static function create(string $type, string $id, array $transports = []): /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getType(): string { @@ -61,6 +62,7 @@ public function getType(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getId(): string { @@ -70,6 +72,7 @@ public function getId(): string /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getTransports(): array { diff --git a/src/webauthn/src/PublicKeyCredentialDescriptorCollection.php b/src/webauthn/src/PublicKeyCredentialDescriptorCollection.php index 4fea6cdb..84446522 100644 --- a/src/webauthn/src/PublicKeyCredentialDescriptorCollection.php +++ b/src/webauthn/src/PublicKeyCredentialDescriptorCollection.php @@ -18,6 +18,7 @@ /** * @implements IteratorAggregate * @deprecated since 4.8.0 and will be removed in 5.0.0. + * @infection-ignore-all */ class PublicKeyCredentialDescriptorCollection implements JsonSerializable, Countable, IteratorAggregate { @@ -53,6 +54,7 @@ public static function create(array $publicKeyCredentialDescriptors): self /** * @deprecated since 4.7.0. Please use the {self::create} instead. + * @infection-ignore-all */ public function add(PublicKeyCredentialDescriptor ...$publicKeyCredentialDescriptors): void { @@ -63,6 +65,7 @@ public function add(PublicKeyCredentialDescriptor ...$publicKeyCredentialDescrip /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function has(string $id): bool { @@ -71,6 +74,7 @@ public function has(string $id): bool /** * @deprecated since 4.7.0. No replacement. + * @infection-ignore-all */ public function remove(string $id): void { @@ -102,6 +106,10 @@ public function jsonSerialize(): array return $this->publicKeyCredentialDescriptors; } + /** + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all + */ public static function createFromString(string $data): self { $data = json_decode($data, true, flags: JSON_THROW_ON_ERROR); @@ -111,6 +119,8 @@ public static function createFromString(string $data): self /** * @param mixed[] $json + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $json): self { diff --git a/src/webauthn/src/PublicKeyCredentialEntity.php b/src/webauthn/src/PublicKeyCredentialEntity.php index 88217d07..844a078b 100644 --- a/src/webauthn/src/PublicKeyCredentialEntity.php +++ b/src/webauthn/src/PublicKeyCredentialEntity.php @@ -16,6 +16,7 @@ public function __construct( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getName(): string { @@ -24,6 +25,7 @@ public function getName(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getIcon(): ?string { diff --git a/src/webauthn/src/PublicKeyCredentialLoader.php b/src/webauthn/src/PublicKeyCredentialLoader.php index c2f1b73f..57afd071 100644 --- a/src/webauthn/src/PublicKeyCredentialLoader.php +++ b/src/webauthn/src/PublicKeyCredentialLoader.php @@ -7,6 +7,7 @@ use ParagonIE\ConstantTime\Base64UrlSafe; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; +use Symfony\Component\Serializer\SerializerInterface; use Throwable; use Webauthn\AttestationStatement\AttestationObjectLoader; use Webauthn\Exception\InvalidDataException; @@ -22,14 +23,17 @@ class PublicKeyCredentialLoader implements CanLogData private LoggerInterface $logger; public function __construct( - private readonly AttestationObjectLoader $attestationObjectLoader + private readonly AttestationObjectLoader $attestationObjectLoader, + private readonly null|SerializerInterface $serializer = null, ) { $this->logger = new NullLogger(); } - public static function create(AttestationObjectLoader $attestationObjectLoader): self - { - return new self($attestationObjectLoader); + public static function create( + AttestationObjectLoader $attestationObjectLoader, + null|SerializerInterface $serializer = null + ): self { + return new self($attestationObjectLoader, $serializer); } public function setLogger(LoggerInterface $logger): void @@ -39,6 +43,8 @@ public function setLogger(LoggerInterface $logger): void /** * @param mixed[] $json + * @deprecated since 4.8.0 and will be removed in 5.0.0. Please use {self::load} instead + * @infection-ignore-all */ public function loadArray(array $json): PublicKeyCredential { @@ -99,9 +105,13 @@ public function load(string $data): PublicKeyCredential 'data' => $data, ]); try { - $json = json_decode($data, true, flags: JSON_THROW_ON_ERROR); + if ($this->serializer === null) { + $json = json_decode($data, true, flags: JSON_THROW_ON_ERROR); + + return $this->loadArray($json); + } - return $this->loadArray($json); + return $this->serializer->deserialize($data, PublicKeyCredential::class, 'json'); } catch (Throwable $throwable) { $this->logger->error('An error occurred', [ 'exception' => $throwable, diff --git a/src/webauthn/src/PublicKeyCredentialOptions.php b/src/webauthn/src/PublicKeyCredentialOptions.php index 0434daf8..55c21049 100644 --- a/src/webauthn/src/PublicKeyCredentialOptions.php +++ b/src/webauthn/src/PublicKeyCredentialOptions.php @@ -30,6 +30,7 @@ public function __construct( /** * @deprecated since 4.7.0. Please use the {self::create} instead. + * @infection-ignore-all */ public function setTimeout(?int $timeout): static { @@ -40,6 +41,7 @@ public function setTimeout(?int $timeout): static /** * @deprecated since 4.7.0. Please use the {self::create} instead. + * @infection-ignore-all */ public function addExtension(AuthenticationExtension $extension): static { @@ -51,6 +53,7 @@ public function addExtension(AuthenticationExtension $extension): static /** * @param AuthenticationExtension[] $extensions * @deprecated since 4.7.0. No replacement. Please use the {self::create} instead. + * @infection-ignore-all */ public function addExtensions(array $extensions): static { @@ -63,6 +66,7 @@ public function addExtensions(array $extensions): static /** * @deprecated since 4.7.0. Please use the {self::create} instead. + * @infection-ignore-all */ public function setExtensions(AuthenticationExtensionsClientInputs $extensions): static { @@ -73,6 +77,7 @@ public function setExtensions(AuthenticationExtensionsClientInputs $extensions): /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getChallenge(): string { @@ -81,6 +86,7 @@ public function getChallenge(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getTimeout(): ?int { @@ -89,16 +95,23 @@ public function getTimeout(): ?int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getExtensions(): AuthenticationExtensionsClientInputs { return $this->extensions; } + /** + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all + */ abstract public static function createFromString(string $data): static; /** * @param mixed[] $json + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all */ abstract public static function createFromArray(array $json): static; } diff --git a/src/webauthn/src/PublicKeyCredentialParameters.php b/src/webauthn/src/PublicKeyCredentialParameters.php index bf518bf4..62cfa053 100644 --- a/src/webauthn/src/PublicKeyCredentialParameters.php +++ b/src/webauthn/src/PublicKeyCredentialParameters.php @@ -32,6 +32,7 @@ public static function createPk(int $alg): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getType(): string { @@ -40,12 +41,17 @@ public function getType(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAlg(): int { return $this->alg; } + /** + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all + */ public static function createFromString(string $data): self { $data = json_decode($data, true, flags: JSON_THROW_ON_ERROR); @@ -55,6 +61,8 @@ public static function createFromString(string $data): self /** * @param mixed[] $json + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $json): self { diff --git a/src/webauthn/src/PublicKeyCredentialRequestOptions.php b/src/webauthn/src/PublicKeyCredentialRequestOptions.php index e51dd751..b54e66a9 100644 --- a/src/webauthn/src/PublicKeyCredentialRequestOptions.php +++ b/src/webauthn/src/PublicKeyCredentialRequestOptions.php @@ -70,6 +70,7 @@ public static function create( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function setRpId(?string $rpId): self { @@ -80,6 +81,7 @@ public function setRpId(?string $rpId): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function allowCredential(PublicKeyCredentialDescriptor $allowCredential): self { @@ -90,6 +92,7 @@ public function allowCredential(PublicKeyCredentialDescriptor $allowCredential): /** * @deprecated since 4.7.0. No replacement. Please use the property directly. + * @infection-ignore-all */ public function allowCredentials(PublicKeyCredentialDescriptor ...$allowCredentials): self { @@ -102,6 +105,7 @@ public function allowCredentials(PublicKeyCredentialDescriptor ...$allowCredenti /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function setUserVerification(?string $userVerification): self { @@ -122,6 +126,7 @@ public function setUserVerification(?string $userVerification): self /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getRpId(): ?string { @@ -131,6 +136,7 @@ public function getRpId(): ?string /** * @return PublicKeyCredentialDescriptor[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAllowCredentials(): array { @@ -139,12 +145,17 @@ public function getAllowCredentials(): array /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getUserVerification(): ?string { return $this->userVerification; } + /** + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all + */ public static function createFromString(string $data): static { $data = json_decode($data, true, flags: JSON_THROW_ON_ERROR); @@ -154,6 +165,8 @@ public static function createFromString(string $data): static /** * @param mixed[] $json + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $json): static { diff --git a/src/webauthn/src/PublicKeyCredentialRpEntity.php b/src/webauthn/src/PublicKeyCredentialRpEntity.php index aef88c95..1720462b 100644 --- a/src/webauthn/src/PublicKeyCredentialRpEntity.php +++ b/src/webauthn/src/PublicKeyCredentialRpEntity.php @@ -24,6 +24,7 @@ public static function create(string $name, ?string $id = null, ?string $icon = /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getId(): ?string { @@ -32,6 +33,8 @@ public function getId(): ?string /** * @param mixed[] $json + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $json): self { diff --git a/src/webauthn/src/PublicKeyCredentialSource.php b/src/webauthn/src/PublicKeyCredentialSource.php index 0f25490d..901bb250 100644 --- a/src/webauthn/src/PublicKeyCredentialSource.php +++ b/src/webauthn/src/PublicKeyCredentialSource.php @@ -74,6 +74,7 @@ public static function create( /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getPublicKeyCredentialId(): string { @@ -87,6 +88,7 @@ public function getPublicKeyCredentialDescriptor(): PublicKeyCredentialDescripto /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAttestationType(): string { @@ -95,6 +97,7 @@ public function getAttestationType(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getTrustPath(): TrustPath { @@ -108,6 +111,7 @@ public function getAttestedCredentialData(): AttestedCredentialData /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getType(): string { @@ -117,6 +121,7 @@ public function getType(): string /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getTransports(): array { @@ -125,6 +130,7 @@ public function getTransports(): array /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getAaguid(): AbstractUid { @@ -133,6 +139,7 @@ public function getAaguid(): AbstractUid /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCredentialPublicKey(): string { @@ -141,6 +148,7 @@ public function getCredentialPublicKey(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getUserHandle(): string { @@ -149,6 +157,7 @@ public function getUserHandle(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCounter(): int { @@ -157,6 +166,7 @@ public function getCounter(): int /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function setCounter(int $counter): void { @@ -166,6 +176,7 @@ public function setCounter(int $counter): void /** * @return array|null * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getOtherUI(): ?array { @@ -175,6 +186,7 @@ public function getOtherUI(): ?array /** * @param array|null $otherUI * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function setOtherUI(?array $otherUI): self { @@ -185,6 +197,8 @@ public function setOtherUI(?array $otherUI): self /** * @param mixed[] $data + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): self { diff --git a/src/webauthn/src/PublicKeyCredentialSourceRepository.php b/src/webauthn/src/PublicKeyCredentialSourceRepository.php index 4c553f1e..5174a418 100644 --- a/src/webauthn/src/PublicKeyCredentialSourceRepository.php +++ b/src/webauthn/src/PublicKeyCredentialSourceRepository.php @@ -6,6 +6,7 @@ /** * @deprecated + * @infection-ignore-all */ interface PublicKeyCredentialSourceRepository { diff --git a/src/webauthn/src/PublicKeyCredentialUserEntity.php b/src/webauthn/src/PublicKeyCredentialUserEntity.php index 7dd9c805..53f147d8 100644 --- a/src/webauthn/src/PublicKeyCredentialUserEntity.php +++ b/src/webauthn/src/PublicKeyCredentialUserEntity.php @@ -33,6 +33,7 @@ public static function create(string $name, string $id, string $displayName, ?st /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getId(): string { @@ -41,12 +42,17 @@ public function getId(): string /** * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getDisplayName(): string { return $this->displayName; } + /** + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all + */ public static function createFromString(string $data): self { $data = json_decode($data, true, flags: JSON_THROW_ON_ERROR); @@ -57,6 +63,8 @@ public static function createFromString(string $data): self /** * @param mixed[] $json + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $json): self { diff --git a/src/webauthn/src/TokenBinding/IgnoreTokenBindingHandler.php b/src/webauthn/src/TokenBinding/IgnoreTokenBindingHandler.php index 623e0663..037fd8c7 100644 --- a/src/webauthn/src/TokenBinding/IgnoreTokenBindingHandler.php +++ b/src/webauthn/src/TokenBinding/IgnoreTokenBindingHandler.php @@ -8,6 +8,7 @@ /** * @deprecated Since 4.3.0 and will be removed in 5.0.0 + * @infection-ignore-all */ final class IgnoreTokenBindingHandler implements TokenBindingHandler { diff --git a/src/webauthn/src/TokenBinding/SecTokenBindingHandler.php b/src/webauthn/src/TokenBinding/SecTokenBindingHandler.php index 3c55ead5..54b6edc9 100644 --- a/src/webauthn/src/TokenBinding/SecTokenBindingHandler.php +++ b/src/webauthn/src/TokenBinding/SecTokenBindingHandler.php @@ -10,6 +10,7 @@ /** * @deprecated Since 4.3.0 and will be removed in 5.0.0 + * @infection-ignore-all */ final class SecTokenBindingHandler implements TokenBindingHandler { diff --git a/src/webauthn/src/TokenBinding/TokenBinding.php b/src/webauthn/src/TokenBinding/TokenBinding.php index 4661c644..cffc6ded 100644 --- a/src/webauthn/src/TokenBinding/TokenBinding.php +++ b/src/webauthn/src/TokenBinding/TokenBinding.php @@ -11,6 +11,7 @@ /** * @deprecated Since 4.3.0 and will be removed in 5.0.0 + * @infection-ignore-all */ class TokenBinding { diff --git a/src/webauthn/src/TokenBinding/TokenBindingHandler.php b/src/webauthn/src/TokenBinding/TokenBindingHandler.php index f5e816be..8c907fdf 100644 --- a/src/webauthn/src/TokenBinding/TokenBindingHandler.php +++ b/src/webauthn/src/TokenBinding/TokenBindingHandler.php @@ -8,6 +8,7 @@ /** * @deprecated Since 4.3.0 and will be removed in 5.0.0 + * @infection-ignore-all */ interface TokenBindingHandler { diff --git a/src/webauthn/src/TokenBinding/TokenBindingNotSupportedHandler.php b/src/webauthn/src/TokenBinding/TokenBindingNotSupportedHandler.php index e64c554e..b2a2c2c1 100644 --- a/src/webauthn/src/TokenBinding/TokenBindingNotSupportedHandler.php +++ b/src/webauthn/src/TokenBinding/TokenBindingNotSupportedHandler.php @@ -9,6 +9,7 @@ /** * @deprecated Since 4.3.0 and will be removed in 5.0.0 + * @infection-ignore-all */ final class TokenBindingNotSupportedHandler implements TokenBindingHandler { diff --git a/src/webauthn/src/TrustPath/CertificateTrustPath.php b/src/webauthn/src/TrustPath/CertificateTrustPath.php index f70d8d43..796511c5 100644 --- a/src/webauthn/src/TrustPath/CertificateTrustPath.php +++ b/src/webauthn/src/TrustPath/CertificateTrustPath.php @@ -29,12 +29,17 @@ public static function create(array $certificates): self /** * @return string[] * @deprecated since 4.7.0. Please use the property directly. + * @infection-ignore-all */ public function getCertificates(): array { return $this->certificates; } + /** + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all + */ public static function createFromArray(array $data): static { array_key_exists('x5c', $data) || throw InvalidTrustPathException::create('The trust path type is invalid'); diff --git a/src/webauthn/src/TrustPath/EcdaaKeyIdTrustPath.php b/src/webauthn/src/TrustPath/EcdaaKeyIdTrustPath.php index 0d6c0f67..d0aa7ffa 100644 --- a/src/webauthn/src/TrustPath/EcdaaKeyIdTrustPath.php +++ b/src/webauthn/src/TrustPath/EcdaaKeyIdTrustPath.php @@ -9,6 +9,7 @@ /** * @deprecated since 4.2.0 and will be removed in 5.0.0. The ECDAA Trust Anchor does no longer exist in Webauthn specification. + * @infection-ignore-all */ final class EcdaaKeyIdTrustPath implements TrustPath { diff --git a/src/webauthn/src/TrustPath/EmptyTrustPath.php b/src/webauthn/src/TrustPath/EmptyTrustPath.php index 8016b62f..74410336 100644 --- a/src/webauthn/src/TrustPath/EmptyTrustPath.php +++ b/src/webauthn/src/TrustPath/EmptyTrustPath.php @@ -21,6 +21,10 @@ public function jsonSerialize(): array ]; } + /** + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all + */ public static function createFromArray(array $data): static { return self::create(); diff --git a/src/webauthn/src/TrustPath/TrustPath.php b/src/webauthn/src/TrustPath/TrustPath.php index fe2124d9..f28097e0 100644 --- a/src/webauthn/src/TrustPath/TrustPath.php +++ b/src/webauthn/src/TrustPath/TrustPath.php @@ -10,6 +10,8 @@ interface TrustPath extends JsonSerializable { /** * @param array $data + * @deprecated since 4.8.0. Please use {Webauthn\Denormalizer\WebauthnSerializerFactory} for converting the object. + * @infection-ignore-all */ public static function createFromArray(array $data): static; } diff --git a/tests/library/Functional/AbstractTestCase.php b/tests/library/Functional/AbstractTestCase.php index 383faf4e..4bb83fe6 100644 --- a/tests/library/Functional/AbstractTestCase.php +++ b/tests/library/Functional/AbstractTestCase.php @@ -32,12 +32,13 @@ use Webauthn\AuthenticationExtensions\ExtensionOutputCheckerHandler; use Webauthn\AuthenticatorAssertionResponseValidator; use Webauthn\AuthenticatorAttestationResponseValidator; +use Webauthn\Denormalizer\WebauthnSerializerFactory; use Webauthn\MetadataService\CertificateChain\CertificateChainValidator; use Webauthn\MetadataService\CertificateChain\PhpCertificateChainValidator; use Webauthn\MetadataService\MetadataStatementRepository as MetadataStatementRepositoryInterface; use Webauthn\MetadataService\Service\ChainedMetadataServices; +use Webauthn\MetadataService\Service\JsonMetadataService; use Webauthn\MetadataService\Service\LocalResourceMetadataService; -use Webauthn\MetadataService\Service\StringMetadataService; use Webauthn\PublicKeyCredentialLoader; use Webauthn\Tests\Bundle\Functional\MockClock; use Webauthn\Tests\MockedPublicKeyCredentialSourceTrait; @@ -76,7 +77,10 @@ protected function setUp(): void protected function getPublicKeyCredentialLoader(): PublicKeyCredentialLoader { if ($this->publicKeyCredentialLoader === null) { - $this->publicKeyCredentialLoader = PublicKeyCredentialLoader::create($this->getAttestationObjectLoader()); + $this->publicKeyCredentialLoader = PublicKeyCredentialLoader::create( + $this->getAttestationObjectLoader(), + (new WebauthnSerializerFactory($this->getAttestationStatementSupportManager(null)))->create() + ); } return $this->publicKeyCredentialLoader; @@ -203,37 +207,18 @@ private function getMetadataStatementRepository(?ClientInterface $client): Metad } foreach ($this->getDistantStatements() as $filename) { $response = new Response(200, [], trim(file_get_contents($filename))); - $client = new Client(new Psr17Factory()); + //$client = new Client(new Psr17Factory()); $client->addResponse($response); $metadataService->addServices( - new StringMetadataService( - '{"legalHeader": "https://fidoalliance.org/metadata/metadata-statement-legal-header/", "aaguid": "b93fd961-f2e6-462f-b122-82002247de78", "description": "Android Authenticator with SafetyNet Attestation", "authenticatorVersion": 1, "protocolFamily": "fido2", "schema": 3, "upv": [{"major": 1, "minor": 0}], "authenticationAlgorithms": ["secp256r1_ecdsa_sha256_raw"], "publicKeyAlgAndEncodings": ["cose"], "attestationTypes": ["basic_full"], "userVerificationDetails": [[{"userVerificationMethod": "faceprint_internal"}], [{"userVerificationMethod": "fingerprint_internal"}], [{"userVerificationMethod": "passcode_internal"}], [{"userVerificationMethod": "pattern_internal"}]], "keyProtection": ["hardware", "tee"], "isKeyRestricted": false, "matcherProtection": ["tee"], "attachmentHint": ["internal"], "tcDisplay": [], "attestationRootCertificates": ["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"], "icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAB7klEQVR4AaWPP2sUQRiHn5mdvexd/plEcvlDCi1E/EMabUWI9jaKWPoV/A7BQhAbG7t8CCUIKQQLuwhCUBsLBSUmGkLudm9n5nWHzMAego3P8Oy9s8vvfd+jzctPz2Ya+Zdbu48mG0ma8Eh8/bF3yWGGwPvV81d7+9/2lpy3Mrty7jswPPz8Yb20lQJ2iain2w9ok02aLURWstxuiHgknnrEK3GERg9poZ7s3CUxl/dvVfrntmRag9BuICJgrXfHnRvAWyJaDxXB+ezCWqX3t6e6i/ri/E1AkdBoLi/cZrL5pqeHb2yvu9RIUKfiWH95IVmmV6eucK1/j8JMIwRo6jNcX77P2vQ6ZEZ7OXreSFA93rnD3Mx6r7YfTxQKGkN4WP8eW7+bz4Z3eHEE9FFZAJXuliXVyUEfif9ZHINW+BQ5fSc+3oTjztTZRkx4LEhtfh1avBMSIkBrA+JvOAohm1AFgJGRpbOoXS/X1KXgHZE4X1Ssxpt18iYImGJiRFWWKCXkBdiR4L0QUEKamIKxhoQZm6fAdMDVjT7cQwBEYh3DSsl4A+trQTwJbUCsT5P+CodTZtYDmNJYcrEDQSChIMsVzoVQ2kLFMCCQFW4AoDbfbRDI7fIi5aAL41jtVNiQiPUjmUBOgAMCm683/ss/TaVXtx4qKMoAAAAASUVORK5CYII=", "authenticatorGetInfo": {"versions": ["FIDO_2_0"], "aaguid": "b93fd961f2e6462fb12282002247de78", "options": {"plat": true, "rk": true, "uv": true}, "transports": ["internal"], "algorithms": [{"type": "public-key", "alg": -7}]}}' - ) - ); - - /* - $metadataService->addServices( - FidoAllianceCompliantMetadataService::create( - new Psr17Factory(), - $client, - 'https://fidoalliance.co.nz/blob.jwt' + new JsonMetadataService( + [ + '{"legalHeader": "https://fidoalliance.org/metadata/metadata-statement-legal-header/", "aaguid": "b93fd961-f2e6-462f-b122-82002247de78", "description": "Android Authenticator with SafetyNet Attestation", "authenticatorVersion": 1, "protocolFamily": "fido2", "schema": 3, "upv": [{"major": 1, "minor": 0}], "authenticationAlgorithms": ["secp256r1_ecdsa_sha256_raw"], "publicKeyAlgAndEncodings": ["cose"], "attestationTypes": ["basic_full"], "userVerificationDetails": [[{"userVerificationMethod": "faceprint_internal"}], [{"userVerificationMethod": "fingerprint_internal"}], [{"userVerificationMethod": "passcode_internal"}], [{"userVerificationMethod": "pattern_internal"}]], "keyProtection": ["hardware", "tee"], "isKeyRestricted": false, "matcherProtection": ["tee"], "attachmentHint": ["internal"], "tcDisplay": [], "attestationRootCertificates": ["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"], "icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAB7klEQVR4AaWPP2sUQRiHn5mdvexd/plEcvlDCi1E/EMabUWI9jaKWPoV/A7BQhAbG7t8CCUIKQQLuwhCUBsLBSUmGkLudm9n5nWHzMAego3P8Oy9s8vvfd+jzctPz2Ya+Zdbu48mG0ma8Eh8/bF3yWGGwPvV81d7+9/2lpy3Mrty7jswPPz8Yb20lQJ2iain2w9ok02aLURWstxuiHgknnrEK3GERg9poZ7s3CUxl/dvVfrntmRag9BuICJgrXfHnRvAWyJaDxXB+ezCWqX3t6e6i/ri/E1AkdBoLi/cZrL5pqeHb2yvu9RIUKfiWH95IVmmV6eucK1/j8JMIwRo6jNcX77P2vQ6ZEZ7OXreSFA93rnD3Mx6r7YfTxQKGkN4WP8eW7+bz4Z3eHEE9FFZAJXuliXVyUEfif9ZHINW+BQ5fSc+3oTjztTZRkx4LEhtfh1avBMSIkBrA+JvOAohm1AFgJGRpbOoXS/X1KXgHZE4X1Ssxpt18iYImGJiRFWWKCXkBdiR4L0QUEKamIKxhoQZm6fAdMDVjT7cQwBEYh3DSsl4A+trQTwJbUCsT5P+CodTZtYDmNJYcrEDQSChIMsVzoVQ2kLFMCCQFW4AoDbfbRDI7fIi5aAL41jtVNiQiPUjmUBOgAMCm683/ss/TaVXtx4qKMoAAAAASUVORK5CYII=", "authenticatorGetInfo": {"versions": ["FIDO_2_0"], "aaguid": "b93fd961f2e6462fb12282002247de78", "options": {"plat": true, "rk": true, "uv": true}, "transports": ["internal"], "algorithms": [{"type": "public-key", "alg": -7}]}}', + ] ) ); - */ } - //$response = new Response(200, [], trim(file_get_contents(__DIR__ . '/../../blob.jwt'))); - //$client = new Client(new Psr17Factory()); - //$client->addResponse($response); - /*$metadataService->addServices( - FidoAllianceCompliantMetadataService::create( - new Psr17Factory(), - $client, - 'https://fidoalliance.co.nz/blob.jwt' - ) - );*/ - $this->metadataStatementRepository = new MetadataStatementRepository($metadataService); } diff --git a/tests/library/Unit/MetadataServiceTest.php b/tests/library/Unit/MetadataServiceTest.php index 3c7394d1..d358752d 100644 --- a/tests/library/Unit/MetadataServiceTest.php +++ b/tests/library/Unit/MetadataServiceTest.php @@ -19,6 +19,7 @@ final class MetadataServiceTest extends TestCase #[Test] public function theMetadataServiceCanLoadUri(): void { + //Given $response = new MockResponse(trim(file_get_contents(__DIR__ . '/../../blob.jwt'))); $client = new MockHttpClient(); $client->setResponseFactory($response); @@ -33,16 +34,19 @@ public function theMetadataServiceCanLoadUri(): void #[Test] public function aMetadataStatementFromAnUriCanBeRetrieved(): void { + //Given $response = new MockResponse(trim(file_get_contents(__DIR__ . '/../../solo.json'))); $client = new MockHttpClient(); $client->setResponseFactory($response); + //When $service = DistantResourceMetadataService::create( null, $client, 'https://raw.githubusercontent.com/solokeys/solo/2.1.0/metadata/Solo-FIDO2-CTAP2-Authenticator.json' ); + //Then static::assertTrue($service->has('8876631b-d4a0-427f-5773-0ec71c9e0279')); $ms = $service->get('8876631b-d4a0-427f-5773-0ec71c9e0279'); static::assertSame('8876631b-d4a0-427f-5773-0ec71c9e0279', $ms->aaguid); diff --git a/tests/library/Unit/Service/FolderResourceMetadataServiceTest.php b/tests/library/Unit/Service/FolderResourceMetadataServiceTest.php index 4ec38ab0..7df1aac3 100644 --- a/tests/library/Unit/Service/FolderResourceMetadataServiceTest.php +++ b/tests/library/Unit/Service/FolderResourceMetadataServiceTest.php @@ -18,7 +18,7 @@ final class FolderResourceMetadataServiceTest extends TestCase public function theListIsCorrect(): void { // Given - $service = new FolderResourceMetadataService(__DIR__ . '/../../mds////'); + $service = FolderResourceMetadataService::create(__DIR__ . '/../../mds////'); // When $list = [...$service->list()]; @@ -34,7 +34,7 @@ public function theListIsCorrect(): void public function theAAGUIDIsSupported(): void { // Given - $service = new FolderResourceMetadataService(__DIR__ . '/../../mds'); + $service = FolderResourceMetadataService::create(__DIR__ . '/../../mds'); // When $isValid = $service->has('9debdbfd-14dd-4e8d-877b-000000000000'); @@ -47,7 +47,7 @@ public function theAAGUIDIsSupported(): void public function theMetadataStatementIsRead(): void { // Given - $service = new FolderResourceMetadataService(__DIR__ . '/../../mds'); + $service = FolderResourceMetadataService::create(__DIR__ . '/../../mds'); // When $mds = $service->get('9debdbfd-14dd-4e8d-877b-4a6e35ddb375'); @@ -64,7 +64,7 @@ public function theMetadataStatementIsInvalid(): void static::expectExceptionMessage('Syntax error'); // Given - $service = new FolderResourceMetadataService(__DIR__ . '/../../mds'); + $service = FolderResourceMetadataService::create(__DIR__ . '/../../mds'); // When $service->get('9debdbfd-14dd-4e8d-877b-000000000000'); diff --git a/tests/symfony/config/config.yml b/tests/symfony/config/config.yml index 3830ac2a..4869a14c 100644 --- a/tests/symfony/config/config.yml +++ b/tests/symfony/config/config.yml @@ -55,6 +55,7 @@ services: - 'webauthn.mds_service' arguments: - '%kernel.project_dir%/tests/metadataStatements' + - '@mds-serializer' # fido_alliance_official: # class: Webauthn\MetadataService\Service\FidoAllianceCompliantMetadataService diff --git a/tests/symfony/functional/Assertion/AssertionTest.php b/tests/symfony/functional/Assertion/AssertionTest.php index dca1209e..5bdbf9ae 100644 --- a/tests/symfony/functional/Assertion/AssertionTest.php +++ b/tests/symfony/functional/Assertion/AssertionTest.php @@ -7,13 +7,14 @@ use ParagonIE\ConstantTime\Base64UrlSafe; use PHPUnit\Framework\Attributes\Test; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\Serializer\SerializerInterface; use Webauthn\AuthenticationExtensions\AuthenticationExtensionsClientInputs; use Webauthn\AuthenticatorAssertionResponse; use Webauthn\AuthenticatorAssertionResponseValidator; use Webauthn\Bundle\Repository\PublicKeyCredentialSourceRepositoryInterface; use Webauthn\Bundle\Service\PublicKeyCredentialRequestOptionsFactory; +use Webauthn\PublicKeyCredential; use Webauthn\PublicKeyCredentialDescriptor; -use Webauthn\PublicKeyCredentialLoader; use Webauthn\PublicKeyCredentialRequestOptions; use Webauthn\Tests\MockedRequestTrait; @@ -46,8 +47,12 @@ public function anAssertionResponseCanBeLoadedAndVerified(): void ) ), ]; - $publicKeyCredential = self::getContainer()->get(PublicKeyCredentialLoader::class)->load( - '{"id":"eHouz_Zi7-BmByHjJ_tx9h4a1WZsK4IzUmgGjkhyOodPGAyUqUp_B9yUkflXY3yHWsNtsrgCXQ3HjAIFUeZB-w","type":"public-key","rawId":"eHouz/Zi7+BmByHjJ/tx9h4a1WZsK4IzUmgGjkhyOodPGAyUqUp/B9yUkflXY3yHWsNtsrgCXQ3HjAIFUeZB+w==","response":{"authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAAew","clientDataJSON":"eyJjaGFsbGVuZ2UiOiJHMEpiTExuZGVmM2EwSXkzUzJzU1FBOHVPNFNPX3plNkZaTUF1UEk2LXhJIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6ODQ0MyIsInR5cGUiOiJ3ZWJhdXRobi5nZXQifQ","signature":"MEUCIEY/vcNkbo/LdMTfLa24ZYLlMMVMRd8zXguHBvqud9AJAiEAwCwpZpvcMaqCrwv85w/8RGiZzE+gOM61ffxmgEDeyhM=","userHandle":null}}' + /** @var SerializerInterface $serializer */ + $serializer = self::getContainer()->get('webauthn-serializer'); + $publicKeyCredential = $serializer->deserialize( + '{"id":"eHouz_Zi7-BmByHjJ_tx9h4a1WZsK4IzUmgGjkhyOodPGAyUqUp_B9yUkflXY3yHWsNtsrgCXQ3HjAIFUeZB-w","type":"public-key","rawId":"eHouz/Zi7+BmByHjJ/tx9h4a1WZsK4IzUmgGjkhyOodPGAyUqUp/B9yUkflXY3yHWsNtsrgCXQ3HjAIFUeZB+w==","response":{"authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAAew","clientDataJSON":"eyJjaGFsbGVuZ2UiOiJHMEpiTExuZGVmM2EwSXkzUzJzU1FBOHVPNFNPX3plNkZaTUF1UEk2LXhJIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6ODQ0MyIsInR5cGUiOiJ3ZWJhdXRobi5nZXQifQ","signature":"MEUCIEY/vcNkbo/LdMTfLa24ZYLlMMVMRd8zXguHBvqud9AJAiEAwCwpZpvcMaqCrwv85w/8RGiZzE+gOM61ffxmgEDeyhM=","userHandle":null}}', + PublicKeyCredential::class, + 'json' ); $publicKeyCredentialSource = self::getContainer()->get( PublicKeyCredentialSourceRepositoryInterface::class diff --git a/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php b/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php index 2745e747..bf711205 100644 --- a/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php +++ b/tests/symfony/functional/Attestation/AdditionalAuthenticatorTest.php @@ -11,6 +11,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\BrowserKit\Cookie; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Serializer\SerializerInterface; use Webauthn\Bundle\Security\Authentication\Token\WebauthnToken; use Webauthn\Bundle\Security\Storage\Item; use Webauthn\Bundle\Security\Storage\OptionsStorage; @@ -158,9 +159,14 @@ public function anExistingUserCanGetOptionsTestItsAuthenticators(): void private function logIn(): void { + /** @var SerializerInterface $serializer */ + $serializer = static::getContainer()->get('webauthn-serializer'); $options = '{"status":"ok","errorMessage":"","rp":{"name":"Webauthn Demo","id":"webauthn.spomky-labs.com"},"pubKeyCredParams":[{"type":"public-key","alg":-8},{"type":"public-key","alg":-7},{"type":"public-key","alg":-43},{"type":"public-key","alg":-35},{"type":"public-key","alg":-36},{"type":"public-key","alg":-257},{"type":"public-key","alg":-258},{"type":"public-key","alg":-259},{"type":"public-key","alg":-37},{"type":"public-key","alg":-38},{"type":"public-key","alg":-39}],"challenge":"EhNVt3T8V12FJvSAc50nhKnZ-MEc-kf84xepDcGyN1g","attestation":"direct","user":{"name":"XY5nn3p_6olTLjoB2Jbb","id":"OTI5ZmJhMmYtMjM2MS00YmM2LWE5MTctYmI3NmFhMTRjN2Y5","displayName":"Bennie Moneypenny"},"authenticatorSelection":{"requireResidentKey":false,"userVerification":"preferred"},"timeout":60000}'; - /** @var PublicKeyCredentialCreationOptions $publicKeyCredentialCreationOptions */ - $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::createFromString($options); + $publicKeyCredentialCreationOptions = $serializer->deserialize( + $options, + PublicKeyCredentialCreationOptions::class, + 'json' + ); $user = User::create( $publicKeyCredentialCreationOptions->user ->name, diff --git a/tests/symfony/functional/Attestation/AttestationTest.php b/tests/symfony/functional/Attestation/AttestationTest.php index 6c4abf78..e80c9759 100644 --- a/tests/symfony/functional/Attestation/AttestationTest.php +++ b/tests/symfony/functional/Attestation/AttestationTest.php @@ -7,18 +7,18 @@ use Cose\Algorithms; use PHPUnit\Framework\Attributes\Test; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Symfony\Component\Serializer\SerializerInterface; use Webauthn\AttestationStatement\AttestationStatement; use Webauthn\AuthenticatorAttestationResponse; use Webauthn\AuthenticatorAttestationResponseValidator; use Webauthn\Bundle\Service\PublicKeyCredentialCreationOptionsFactory; use Webauthn\MetadataService\Exception\CertificateChainException; +use Webauthn\PublicKeyCredential; use Webauthn\PublicKeyCredentialCreationOptions; use Webauthn\PublicKeyCredentialDescriptor; -use Webauthn\PublicKeyCredentialLoader; use Webauthn\PublicKeyCredentialParameters; use Webauthn\PublicKeyCredentialRpEntity; use Webauthn\PublicKeyCredentialUserEntity; -use Webauthn\Tests\Bundle\Functional\MockClientCallback; use Webauthn\Tests\Bundle\Functional\PublicKeyCredentialSourceRepository; use Webauthn\Tests\MockedRequestTrait; use Webauthn\TrustPath\CertificateTrustPath; @@ -49,8 +49,12 @@ public function foo(): void ), [PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_ES256)] ); - $publicKeyCredential = self::$kernel->getContainer()->get(PublicKeyCredentialLoader::class)->load( - '{"id":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB_MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1-RIuTF9DUtEJZEEK","type":"public-key","rawId":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB/MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1+RIuTF9DUtEJZEEK","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOiI5V3FncFJJWXZHTUNVWWlGVDIwbzFVN2hTRDE5M2sxMXp1NHRLUDd3UmNyRTI2enMxemM0TEh5UGludlBHUzg2d3U2YkR2cHdidDhYcDJiUTNWQlJTUSIsImNsaWVudEV4dGVuc2lvbnMiOnt9LCJoYXNoQWxnb3JpdGhtIjoiU0hBLTI1NiIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0Ojg0NDMiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0","attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjkSZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2NBAAAAAAAAAAAAAAAAAAAAAAAAAAAAYJjIobiMfS7pLMMQTjIzBw3+hADjTsu6nVoWkEO3TrVYkdnFQfzDW2cVEYtnL4ErykiC295iEnvZTzRvbGIKI7mOYjYp2DoOoUVcZptFbLLjRtqZtfkSLkxfQ1LRCWRBCqUBAgMmIAEhWCAcPxwKyHADVjTgTsat4R/Jax6PWte50A8ZasMm4w6RxCJYILt0FCiGwC6rBrh3ySNy0yiUjZpNGAhW+aM9YYyYnUTJ"}}' + /** @var SerializerInterface $serializer */ + $serializer = self::getContainer()->get('webauthn-serializer'); + $publicKeyCredential = $serializer->deserialize( + '{"id":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB_MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1-RIuTF9DUtEJZEEK","type":"public-key","rawId":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB/MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1+RIuTF9DUtEJZEEK","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOiI5V3FncFJJWXZHTUNVWWlGVDIwbzFVN2hTRDE5M2sxMXp1NHRLUDd3UmNyRTI2enMxemM0TEh5UGludlBHUzg2d3U2YkR2cHdidDhYcDJiUTNWQlJTUSIsImNsaWVudEV4dGVuc2lvbnMiOnt9LCJoYXNoQWxnb3JpdGhtIjoiU0hBLTI1NiIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0Ojg0NDMiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0","attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjkSZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2NBAAAAAAAAAAAAAAAAAAAAAAAAAAAAYJjIobiMfS7pLMMQTjIzBw3+hADjTsu6nVoWkEO3TrVYkdnFQfzDW2cVEYtnL4ErykiC295iEnvZTzRvbGIKI7mOYjYp2DoOoUVcZptFbLLjRtqZtfkSLkxfQ1LRCWRBCqUBAgMmIAEhWCAcPxwKyHADVjTgTsat4R/Jax6PWte50A8ZasMm4w6RxCJYILt0FCiGwC6rBrh3ySNy0yiUjZpNGAhW+aM9YYyYnUTJ"}}', + PublicKeyCredential::class, + 'json' ); $descriptor = $publicKeyCredential->getPublicKeyCredentialDescriptor(); static::assertSame(PublicKeyCredentialDescriptor::CREDENTIAL_TYPE_PUBLIC_KEY, $descriptor->type); @@ -91,8 +95,12 @@ public function anAttestationResponseCanBeLoadedAndVerified(): void ), [PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_ES256)] ); - $publicKeyCredential = self::$kernel->getContainer()->get(PublicKeyCredentialLoader::class)->load( - '{"id":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB_MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1-RIuTF9DUtEJZEEK","type":"public-key","rawId":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB/MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1+RIuTF9DUtEJZEEK","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOiI5V3FncFJJWXZHTUNVWWlGVDIwbzFVN2hTRDE5M2sxMXp1NHRLUDd3UmNyRTI2enMxemM0TEh5UGludlBHUzg2d3U2YkR2cHdidDhYcDJiUTNWQlJTUSIsImNsaWVudEV4dGVuc2lvbnMiOnt9LCJoYXNoQWxnb3JpdGhtIjoiU0hBLTI1NiIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0Ojg0NDMiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0","attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjkSZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2NBAAAAAAAAAAAAAAAAAAAAAAAAAAAAYJjIobiMfS7pLMMQTjIzBw3+hADjTsu6nVoWkEO3TrVYkdnFQfzDW2cVEYtnL4ErykiC295iEnvZTzRvbGIKI7mOYjYp2DoOoUVcZptFbLLjRtqZtfkSLkxfQ1LRCWRBCqUBAgMmIAEhWCAcPxwKyHADVjTgTsat4R/Jax6PWte50A8ZasMm4w6RxCJYILt0FCiGwC6rBrh3ySNy0yiUjZpNGAhW+aM9YYyYnUTJ"}}' + /** @var SerializerInterface $serializer */ + $serializer = self::getContainer()->get('webauthn-serializer'); + $publicKeyCredential = $serializer->deserialize( + '{"id":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB_MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1-RIuTF9DUtEJZEEK","type":"public-key","rawId":"mMihuIx9LukswxBOMjMHDf6EAONOy7qdWhaQQ7dOtViR2cVB/MNbZxURi2cvgSvKSILb3mISe9lPNG9sYgojuY5iNinYOg6hRVxmm0VssuNG2pm1+RIuTF9DUtEJZEEK","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOiI5V3FncFJJWXZHTUNVWWlGVDIwbzFVN2hTRDE5M2sxMXp1NHRLUDd3UmNyRTI2enMxemM0TEh5UGludlBHUzg2d3U2YkR2cHdidDhYcDJiUTNWQlJTUSIsImNsaWVudEV4dGVuc2lvbnMiOnt9LCJoYXNoQWxnb3JpdGhtIjoiU0hBLTI1NiIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0Ojg0NDMiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0","attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjkSZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2NBAAAAAAAAAAAAAAAAAAAAAAAAAAAAYJjIobiMfS7pLMMQTjIzBw3+hADjTsu6nVoWkEO3TrVYkdnFQfzDW2cVEYtnL4ErykiC295iEnvZTzRvbGIKI7mOYjYp2DoOoUVcZptFbLLjRtqZtfkSLkxfQ1LRCWRBCqUBAgMmIAEhWCAcPxwKyHADVjTgTsat4R/Jax6PWte50A8ZasMm4w6RxCJYILt0FCiGwC6rBrh3ySNy0yiUjZpNGAhW+aM9YYyYnUTJ"}}', + PublicKeyCredential::class, + 'json' ); $descriptor = $publicKeyCredential->getPublicKeyCredentialDescriptor(); static::assertSame(PublicKeyCredentialDescriptor::CREDENTIAL_TYPE_PUBLIC_KEY, $descriptor->type); @@ -118,14 +126,22 @@ public function anAttestationResponseCanBeLoadedAndVerified(): void #[Test] public function aFullCertificateChainShouldNotBeUsedForThisSelfAttestation(): void { - self::getContainer()->get(MockClientCallback::class); - $this->expectException(CertificateChainException::class); - $this->expectExceptionMessage('Unable to validate the certificate chain.'); + // Given + /** @var SerializerInterface $serializer */ + $serializer = self::getContainer()->get('webauthn-serializer'); + $options = '{"status":"ok","errorMessage":"","rp":{"name":"Webauthn Demo","id":"webauthn.spomky-labs.com"},"pubKeyCredParams":[{"type":"public-key","alg":-8},{"type":"public-key","alg":-7},{"type":"public-key","alg":-43},{"type":"public-key","alg":-35},{"type":"public-key","alg":-36},{"type":"public-key","alg":-257},{"type":"public-key","alg":-258},{"type":"public-key","alg":-259},{"type":"public-key","alg":-37},{"type":"public-key","alg":-38},{"type":"public-key","alg":-39}],"challenge":"h8lQZpu-S0rTLOOeAr7BeWoPPTkhtqcEzlHizEyzVeQ","attestation":"direct","user":{"name":"fwOcfew16ujF_p7Hl5eh","id":"ZTc4N2YzZmItMDgwZS00ZDNjLTlhZDItYmE3OTAwYTVlNTg1","displayName":"Gretchen Mo"},"authenticatorSelection":{"requireResidentKey":false,"userVerification":"preferred"},"timeout":60000}'; + $publicKeyCredentialCreationOptions = $serializer->deserialize( + $options, + PublicKeyCredentialCreationOptions::class, + 'json' + ); + $result = '{"id":"icUOVRPT8oO9WQhpaE90z6jlKCac8JdnczpH6t694JQ","rawId":"icUOVRPT8oO9WQhpaE90z6jlKCac8JdnczpH6t694JQ","response":{"attestationObject":"o2NmbXRmcGFja2VkZ2F0dFN0bXSjY2FsZyZjc2lnWEYwRAIgZrOe5oDaahYFZM1iH9P_NJpbwN1FY0swi0d8pGImrwYCIBggQ17iKyGOnqVYumhwL_escFlB27AETQl0yLO8nmuWY3g1Y4FZBEUwggRBMIICKaADAgECAgEBMA0GCSqGSIb3DQEBCwUAMIGhMRgwFgYDVQQDDA9GSURPMiBURVNUIFJPT1QxMTAvBgkqhkiG9w0BCQEWImNvbmZvcm1hbmNlLXRvb2xzQGZpZG9hbGxpYW5jZS5vcmcxFjAUBgNVBAoMDUZJRE8gQWxsaWFuY2UxDDAKBgNVBAsMA0NXRzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1ZMRIwEAYDVQQHDAlXYWtlZmllbGQwHhcNMTgwNTIzMTQzOTQzWhcNMjgwNTIwMTQzOTQzWjCBwjEjMCEGA1UEAwwaRklETzIgQkFUQ0ggS0VZIHByaW1lMjU2djExMTAvBgkqhkiG9w0BCQEWImNvbmZvcm1hbmNlLXRvb2xzQGZpZG9hbGxpYW5jZS5vcmcxFjAUBgNVBAoMDUZJRE8gQWxsaWFuY2UxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNWTESMBAGA1UEBwwJV2FrZWZpZWxkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETzpeXqtsH7yul_bfZEmWdix773IAQCp2xvIw9lVvF6qZm1l_xL9Qiq-OnvDNAT9aub0nkUvwgEN4y8yxG4m1RqMsMCowCQYDVR0TBAIwADAdBgNVHQ4EFgQUVk33wPjGVbahH2xNGfO_QeL9AXkwDQYJKoZIhvcNAQELBQADggIBAIfdoJ_4IIkF0S1Bzgmk6dR6XAYbDsPGcEyiQccGCvMnEOf0EVwXDEYvGsVXsR9h6FA04P7vg5Lx9lGBmI1_0QMYBiIeHT4Kyl8FZ3bTMIiOUJ0MFzKHCrc8snrkkL-iDcJP0AriS-SzgMj7TVFjE2_1LwnHWFo7WWBTnmEEivU_-nbVkqelwISE-MH9wgWscmovmIkZ9534teeL1K6rbg4eenjgyu_iHs4PZ6W7nJZ918Vv5EYbZNhREUgZgaKOyKLT3fDRkwE58FL7der8Osd5ltmus2RjjnmAkJnl5Xzc2u30n39QXRVkeX-HCdIBQL9ve03-XRmUL2Q9w3MkPTiXid0UEPYp19DYcZNfunJtYtnvIfYEze6LY6mJpxo7N3s4T3WsdgHa5nJDuN2DbnIX0zxAj00cz-K0KN0G8Bi3hAJPx1fqCZmIgZHYX9hdkCzJu0nXqmdSY4NVtbzSU9vPL49RBhfv2il4P27owGivOv2DTwSWlvUXcOBJ3xVIuWxHZA-WUqXgBwkMwg59kc5AY7Nq0xXuKkRVFrQvkWeMBakce9I1yyMPgK6XnraY7cyUjakLKj5RL6cjMbldmY567gNv8rD90Q86jbO0fCVTSoontEQGxu3reN1C2XAu6IsfCSmLCesA5l_Bssu71jPi0vV4mVB9-7BL8CiWzPscaGF1dGhEYXRhWKSWBOqCgk6YpK2hS0Ri0Nc6jsRpEw2pGxkwdFkin3SjWUEAAABTKy7LtFm0RPqGjaBySF2K4AAgicUOVRPT8oO9WQhpaE90z6jlKCac8JdnczpH6t694JSlAQIDJiABIVgg_YaefIjxYBFvUYvXdQCxl-2AbCSIAOCwxt_m_qQ-SeEiWCBiSYa6JD1eR7jbJOppgUdyIle1hmviAK-UvU7_-SZbCg","clientDataJSON":"eyJvcmlnaW4iOiJodHRwczovL3dlYmF1dGhuLnNwb21reS1sYWJzLmNvbSIsImNoYWxsZW5nZSI6Img4bFFacHUtUzByVExPT2VBcjdCZVdvUFBUa2h0cWNFemxIaXpFeXpWZVEiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0"},"type":"public-key"}'; - $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::createFromString($options); - $publicKeyCredential = self::$kernel->getContainer()->get(PublicKeyCredentialLoader::class)->load($result); + $publicKeyCredential = $serializer->deserialize($result, PublicKeyCredential::class, 'json'); $descriptor = $publicKeyCredential->getPublicKeyCredentialDescriptor(); + + // Then static::assertSame(PublicKeyCredentialDescriptor::CREDENTIAL_TYPE_PUBLIC_KEY, $descriptor->type); static::assertSame( hex2bin('89c50e5513d3f283bd590869684f74cfa8e528269cf09767733a47eadebde094'), @@ -136,6 +152,10 @@ public function aFullCertificateChainShouldNotBeUsedForThisSelfAttestation(): vo static::assertInstanceOf(AuthenticatorAttestationResponse::class, $response); static::assertSame(AttestationStatement::TYPE_BASIC, $response->attestationObject ->attStmt ->type); static::assertInstanceOf(CertificateTrustPath::class, $response->attestationObject ->attStmt ->trustPath); + $this->expectException(CertificateChainException::class); + $this->expectExceptionMessage('Unable to validate the certificate chain.'); + + // When self::$kernel->getContainer()->get(AuthenticatorAttestationResponseValidator::class)->check( $publicKeyCredential->response, $publicKeyCredentialCreationOptions, @@ -146,41 +166,62 @@ public function aFullCertificateChainShouldNotBeUsedForThisSelfAttestation(): vo #[Test] public function eddsa(): void { + // Given + /** @var SerializerInterface $serializer */ + $serializer = self::getContainer()->get('webauthn-serializer'); + self::bootKernel(); self::$kernel->getContainer()->get(PublicKeyCredentialSourceRepository::class)->clearCredentials(); $options = '{"status":"ok","errorMessage":"","rp":{"name":"Webauthn Demo","id":"webauthn.spomky-labs.com"},"pubKeyCredParams":[{"type":"public-key","alg":-8},{"type":"public-key","alg":-7},{"type":"public-key","alg":-43},{"type":"public-key","alg":-35},{"type":"public-key","alg":-36},{"type":"public-key","alg":-257},{"type":"public-key","alg":-258},{"type":"public-key","alg":-259},{"type":"public-key","alg":-37},{"type":"public-key","alg":-38},{"type":"public-key","alg":-39}],"challenge":"EhNVt3T8V12FJvSAc50nhKnZ-MEc-kf84xepDcGyN1g","attestation":"direct","user":{"name":"XY5nn3p_6olTLjoB2Jbb","id":"OTI5ZmJhMmYtMjM2MS00YmM2LWE5MTctYmI3NmFhMTRjN2Y5","displayName":"Bennie Moneypenny"},"authenticatorSelection":{"requireResidentKey":false,"userVerification":"preferred"},"timeout":60000}'; - $result = '{"id":"WT7a99M1zA3XUBBvEwXqPzP0C3zNoS_SpmMpv2sG2YM","rawId":"WT7a99M1zA3XUBBvEwXqPzP0C3zNoS/SpmMpv2sG2YM","response":{"attestationObject":"o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZydjc2lnWECRl1RciDxSF7hkhJbqVJeryUIFrX7r6QQMQq8bIP4wYRA6f96iOO4wiOo34l65kZ5v1erxSmIaH56VySUSMusEaGF1dGhEYXRhWIGWBOqCgk6YpK2hS0Ri0Nc6jsRpEw2pGxkwdFkin3SjWUEAAAAykd_q15WeRHWtJpsNSCvgiQAgWT7a99M1zA3XUBBvEwXqPzP0C3zNoS_SpmMpv2sG2YOkAQEDJyAGIVgg4smTlXUJnAP_RqNWNv2Eqkh8I7ZDS0IuSgotbPygd9k","clientDataJSON":"eyJvcmlnaW4iOiJodHRwczovL3dlYmF1dGhuLnNwb21reS1sYWJzLmNvbSIsImNoYWxsZW5nZSI6IkVoTlZ0M1Q4VjEyRkp2U0FjNTBuaEtuWi1NRWMta2Y4NHhlcERjR3lOMWciLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0"},"type":"public-key"}'; - $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::createFromString($options); - $publicKeyCredential = self::$kernel->getContainer()->get(PublicKeyCredentialLoader::class)->load($result); - $descriptor = $publicKeyCredential->getPublicKeyCredentialDescriptor(); - static::assertSame(PublicKeyCredentialDescriptor::CREDENTIAL_TYPE_PUBLIC_KEY, $descriptor->type); - static::assertSame( - hex2bin('593edaf7d335cc0dd750106f1305ea3f33f40b7ccda12fd2a66329bf6b06d983'), - $descriptor->id + $publicKeyCredentialCreationOptions = $serializer->deserialize( + $options, + PublicKeyCredentialCreationOptions::class, + 'json' ); - static::assertSame([], $descriptor->transports); - $response = $publicKeyCredential->response; - static::assertInstanceOf(AuthenticatorAttestationResponse::class, $response); - static::assertSame(AttestationStatement::TYPE_SELF, $response->attestationObject->attStmt->type); - static::assertInstanceOf(EmptyTrustPath::class, $response->attestationObject ->attStmt ->trustPath); - self::$kernel->getContainer()->get(AuthenticatorAttestationResponseValidator::class)->check( + + $result = '{"id":"WT7a99M1zA3XUBBvEwXqPzP0C3zNoS_SpmMpv2sG2YM","rawId":"WT7a99M1zA3XUBBvEwXqPzP0C3zNoS/SpmMpv2sG2YM","response":{"attestationObject":"o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZydjc2lnWECRl1RciDxSF7hkhJbqVJeryUIFrX7r6QQMQq8bIP4wYRA6f96iOO4wiOo34l65kZ5v1erxSmIaH56VySUSMusEaGF1dGhEYXRhWIGWBOqCgk6YpK2hS0Ri0Nc6jsRpEw2pGxkwdFkin3SjWUEAAAAykd_q15WeRHWtJpsNSCvgiQAgWT7a99M1zA3XUBBvEwXqPzP0C3zNoS_SpmMpv2sG2YOkAQEDJyAGIVgg4smTlXUJnAP_RqNWNv2Eqkh8I7ZDS0IuSgotbPygd9k","clientDataJSON":"eyJvcmlnaW4iOiJodHRwczovL3dlYmF1dGhuLnNwb21reS1sYWJzLmNvbSIsImNoYWxsZW5nZSI6IkVoTlZ0M1Q4VjEyRkp2U0FjNTBuaEtuWi1NRWMta2Y4NHhlcERjR3lOMWciLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0"},"type":"public-key"}'; + $publicKeyCredential = $serializer->deserialize($result, PublicKeyCredential::class, 'json'); + $publicKeyCredential->getPublicKeyCredentialDescriptor(); + + // When + $result = self::$kernel->getContainer()->get(AuthenticatorAttestationResponseValidator::class)->check( $publicKeyCredential->response, $publicKeyCredentialCreationOptions, 'webauthn.spomky-labs.com' ); + + // Then + static::assertSame('929fba2f-2361-4bc6-a917-bb76aa14c7f9', $result->userHandle); + static::assertSame(50, $result->counter); + static::assertSame('91dfead7-959e-4475-ad26-9b0d482be089', $result->aaguid->toRfc4122()); + static::assertSame( + hex2bin('593edaf7d335cc0dd750106f1305ea3f33f40b7ccda12fd2a66329bf6b06d983'), + $result->publicKeyCredentialId + ); } #[Test] public function certificateExpired(): void { + // Given + /** @var SerializerInterface $serializer */ + $serializer = self::getContainer()->get('webauthn-serializer'); + + //Then $this->expectException(CertificateChainException::class); $this->expectExceptionMessage('Unable to validate the certificate chain.'); - self::bootKernel(); + $options = '{"status":"ok","errorMessage":"","rp":{"name":"Webauthn Demo","id":"webauthn.spomky-labs.com"},"pubKeyCredParams":[{"type":"public-key","alg":-8},{"type":"public-key","alg":-7},{"type":"public-key","alg":-43},{"type":"public-key","alg":-35},{"type":"public-key","alg":-36},{"type":"public-key","alg":-257},{"type":"public-key","alg":-258},{"type":"public-key","alg":-259},{"type":"public-key","alg":-37},{"type":"public-key","alg":-38},{"type":"public-key","alg":-39}],"challenge":"vK4TDySRYWO-ZMLS19rRzbuqSDBz-QZRLBb9MB6TVek","attestation":"direct","user":{"name":"KO5UZZdhgkrDan8uypFD","id":"MWY1ODk4M2MtN2JlMi00ZWIxLTllMjMtMDAwZWQwMTk3OGZh","displayName":"Sharyl Seguin"},"authenticatorSelection":{"requireResidentKey":false,"userVerification":"preferred"},"timeout":60000}'; + $publicKeyCredentialCreationOptions = $serializer->deserialize( + $options, + PublicKeyCredentialCreationOptions::class, + 'json' + ); + $result = '{"id":"u0VyY10Mp_r0HnjCRx-uVL_uyzMAK300KmtFkwQVfJo","rawId":"u0VyY10Mp_r0HnjCRx-uVL_uyzMAK300KmtFkwQVfJo","response":{"attestationObject":"o2NmbXRmcGFja2VkZ2F0dFN0bXSjY2FsZyZjc2lnWEYwRAIgYvfDLmK6e21CGl-CkF9S3l54VS6Ju0sWJ5VwUB68a_4CIAZoj-gRGrTb3jcYH1u_KtI_mwo4IYdAjAnSUsMtMTCrY3g1Y4FZBE0wggRJMIICMaADAgECAgEBMA0GCSqGSIb3DQEBCwUAMIGhMRgwFgYDVQQDDA9GSURPMiBURVNUIFJPT1QxMTAvBgkqhkiG9w0BCQEWImNvbmZvcm1hbmNlLXRvb2xzQGZpZG9hbGxpYW5jZS5vcmcxFjAUBgNVBAoMDUZJRE8gQWxsaWFuY2UxDDAKBgNVBAsMA0NXRzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1ZMRIwEAYDVQQHDAlXYWtlZmllbGQwHhcNMTgwNTIyMTIxODQ1WhcNMTgwNTIzMTIxODQ1WjCByjErMCkGA1UEAwwiRklETzIgRVhQSVJFRCBCQVRDSCBLRVkgcHJpbWUyNTZ2MTExMC8GCSqGSIb3DQEJARYiY29uZm9ybWFuY2UtdG9vbHNAZmlkb2FsbGlhbmNlLm9yZzEWMBQGA1UECgwNRklETyBBbGxpYW5jZTEiMCAGA1UECwwZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1ZMRIwEAYDVQQHDAlXYWtlZmllbGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARJdux_JC6uTGWho8eSKpuQmzRfF01V_cVGUEBKY82G-NB3J6-k76qTGvaQKG5HXQyDCiZIJGixIAA1xmoNnsRnoywwKjAJBgNVHRMEAjAAMB0GA1UdDgQWBBSv7KtPuy2pPcSktaEeir5sFjxvfTANBgkqhkiG9w0BAQsFAAOCAgEAIUx9saJ5pXSEDS6Cb-wyNp2tPJPaSQKEFsUnrzETsY5Bm0Hc1wENHf2pUhnqooXDcfWuhDK2_Wt86AZ6q8p3mv91YKXyJfZXNAksMXONE-nKDRkijrNydqdzL18D6I5aWwyj_icAneDFuzABIevrxohsCkVYDF0tdNDsKGRHwaRv2JWGp8atChwa0cAkYYQ2OYIylVkuxauUxfx8BUU41w8fknqa_Ih1jpyGfX6yL6EDYH5nSAm6fYne1zgZkhvKVS1_RNPIUXD9YssmXOlbPpMqXo-RZH34zNAzblIFZixTouzRBggitz1vNDu5IcCuyxutVEGiP_cEt9AAF-YEs7yUmKLdgWeM5AjGXL2Pq4NSWjHesBNHyGqFgzPgHQcMVDvkR4uZswh6v-cA9cXB8bCecionoga_7FZPg3uexkjj_I9LfOuHDshhO1vWNKF07oBW-YlXOYj2eOkv6hdclPmXbbrjNMJR1xY5Poev9OCaq6u6ZYf4yjaqDWjN--hbUUUN3juIhwo748Wg_ds7HXw03bfhAt33MVq6-THDZWEJ9rf8K6gBysrdAreBkOLND0c5zhk5HV3RAn8QYG3PKF7dkktLvbdBQvSfDbmu_gzcaXRn2pMs6kri6tYR82yvnRZZC6AQILw3rh9U3KIf76aj7lyHSNNOHKZC5WwLuURoYXV0aERhdGFYpJYE6oKCTpikraFLRGLQ1zqOxGkTDakbGTB0WSKfdKNZQQAAACkyatzwDO9G0JOSmNbEqEpyACC7RXJjXQyn-vQeeMJHH65Uv-7LMwArfTQqa0WTBBV8mqUBAgMmIAEhWCAgvqy102Xgb_tOUcC9I6vCksCFSp7sMpbEuZtGO1MI_CJYIGgStwNV2pziVj7naI50SSZtu7wsPurxtqmLzKb92op3","clientDataJSON":"eyJvcmlnaW4iOiJodHRwczovL3dlYmF1dGhuLnNwb21reS1sYWJzLmNvbSIsImNoYWxsZW5nZSI6InZLNFREeVNSWVdPLVpNTFMxOXJSemJ1cVNEQnotUVpSTEJiOU1CNlRWZWsiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0"},"type":"public-key"}'; - $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::createFromString($options); - $publicKeyCredential = self::$kernel->getContainer()->get(PublicKeyCredentialLoader::class)->load($result); + $publicKeyCredential = $serializer->deserialize($result, PublicKeyCredential::class, 'json'); $descriptor = $publicKeyCredential->getPublicKeyCredentialDescriptor(); + static::assertSame(PublicKeyCredentialDescriptor::CREDENTIAL_TYPE_PUBLIC_KEY, $descriptor->type); static::assertSame( hex2bin('bb4572635d0ca7faf41e78c2471fae54bfeecb33002b7d342a6b459304157c9a'), @@ -191,6 +232,8 @@ public function certificateExpired(): void static::assertInstanceOf(AuthenticatorAttestationResponse::class, $response); static::assertSame(AttestationStatement::TYPE_BASIC, $response->attestationObject ->attStmt ->type); static::assertInstanceOf(CertificateTrustPath::class, $response->attestationObject ->attStmt ->trustPath); + + // When self::$kernel->getContainer()->get(AuthenticatorAttestationResponseValidator::class)->check( $publicKeyCredential->response, $publicKeyCredentialCreationOptions, diff --git a/tests/symfony/functional/Attestation/PackedAttestationStatementTest.php b/tests/symfony/functional/Attestation/PackedAttestationStatementTest.php index 64ebc570..8bdaac57 100644 --- a/tests/symfony/functional/Attestation/PackedAttestationStatementTest.php +++ b/tests/symfony/functional/Attestation/PackedAttestationStatementTest.php @@ -7,12 +7,13 @@ use Cose\Algorithms; use PHPUnit\Framework\Attributes\Test; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Symfony\Component\Serializer\SerializerInterface; use Webauthn\AttestationStatement\AttestationStatement; use Webauthn\AuthenticatorAttestationResponse; use Webauthn\AuthenticatorAttestationResponseValidator; +use Webauthn\PublicKeyCredential; use Webauthn\PublicKeyCredentialCreationOptions; use Webauthn\PublicKeyCredentialDescriptor; -use Webauthn\PublicKeyCredentialLoader; use Webauthn\PublicKeyCredentialParameters; use Webauthn\PublicKeyCredentialRpEntity; use Webauthn\PublicKeyCredentialUserEntity; @@ -41,8 +42,12 @@ public function aPackedAttestationWithSelfStatementCanBeVerified(): void [PublicKeyCredentialParameters::create('public-key', Algorithms::COSE_ALGORITHM_ES256)] ); $publicKeyCredentialCreationOptions->attestation = PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT; - $publicKeyCredential = self::$kernel->getContainer()->get(PublicKeyCredentialLoader::class)->load( - '{"id":"AFkzwaxVuCUz4qFPaNAgnYgoZKKTtvGIAaIASAbnlHGy8UktdI_jN0CetpIkiw9--R0AF9a6OJnHD-G4aIWur-Pxj-sI9xDE-AVeQKve","type":"public-key","rawId":"AFkzwaxVuCUz4qFPaNAgnYgoZKKTtvGIAaIASAbnlHGy8UktdI/jN0CetpIkiw9++R0AF9a6OJnHD+G4aIWur+Pxj+sI9xDE+AVeQKve","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOiJvRlVHaFVldlFIWDdKNm80T0ZhdTVQYm5jQ0FUYUh3akhETEx6Q1RwaXl3Iiwib3JpZ2luIjoiaHR0cHM6Ly9zcG9ta3ktd2ViYXV0aG4uaGVyb2t1YXBwLmNvbSIsInR5cGUiOiJ3ZWJhdXRobi5jcmVhdGUifQ","attestationObject":"o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIgAMCQZYRl2cA+ab2MB3OGBCbq3j62rSubwhaCVSHJvKMCIQD0mMLs/5jjwd0KxYzb9/iM15T1gJ3L1Uv5BnMtQtVYBmhhdXRoRGF0YVjStIXbbgSILsWHHbR0Fjkl96X4ROZYLvVtOopBWCQoAqpFXE8bBwAAAAAAAAAAAAAAAAAAAAAATgBZM8GsVbglM+KhT2jQIJ2IKGSik7bxiAGiAEgG55RxsvFJLXSP4zdAnraSJIsPfvkdABfWujiZxw/huGiFrq/j8Y/rCPcQxPgFXkCr3qUBAgMmIAEhWCBOSwRVQxXPb76nvmQ2HQ8i5Bin8M4zfZCqIlKXrcxxmyJYIOFCAZ9+rRhklvn1nk2TahaCvpH96emEuKoGxpEObvQg"}}' + /** @var SerializerInterface $serializer */ + $serializer = self::getContainer()->get('webauthn-serializer'); + $publicKeyCredential = $serializer->deserialize( + '{"id":"AFkzwaxVuCUz4qFPaNAgnYgoZKKTtvGIAaIASAbnlHGy8UktdI_jN0CetpIkiw9--R0AF9a6OJnHD-G4aIWur-Pxj-sI9xDE-AVeQKve","type":"public-key","rawId":"AFkzwaxVuCUz4qFPaNAgnYgoZKKTtvGIAaIASAbnlHGy8UktdI/jN0CetpIkiw9++R0AF9a6OJnHD+G4aIWur+Pxj+sI9xDE+AVeQKve","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOiJvRlVHaFVldlFIWDdKNm80T0ZhdTVQYm5jQ0FUYUh3akhETEx6Q1RwaXl3Iiwib3JpZ2luIjoiaHR0cHM6Ly9zcG9ta3ktd2ViYXV0aG4uaGVyb2t1YXBwLmNvbSIsInR5cGUiOiJ3ZWJhdXRobi5jcmVhdGUifQ","attestationObject":"o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIgAMCQZYRl2cA+ab2MB3OGBCbq3j62rSubwhaCVSHJvKMCIQD0mMLs/5jjwd0KxYzb9/iM15T1gJ3L1Uv5BnMtQtVYBmhhdXRoRGF0YVjStIXbbgSILsWHHbR0Fjkl96X4ROZYLvVtOopBWCQoAqpFXE8bBwAAAAAAAAAAAAAAAAAAAAAATgBZM8GsVbglM+KhT2jQIJ2IKGSik7bxiAGiAEgG55RxsvFJLXSP4zdAnraSJIsPfvkdABfWujiZxw/huGiFrq/j8Y/rCPcQxPgFXkCr3qUBAgMmIAEhWCBOSwRVQxXPb76nvmQ2HQ8i5Bin8M4zfZCqIlKXrcxxmyJYIOFCAZ9+rRhklvn1nk2TahaCvpH96emEuKoGxpEObvQg"}}', + PublicKeyCredential::class, + 'json' ); $descriptor = $publicKeyCredential->getPublicKeyCredentialDescriptor(); static::assertSame(PublicKeyCredentialDescriptor::CREDENTIAL_TYPE_PUBLIC_KEY, $descriptor->type); diff --git a/tests/symfony/functional/MetadataService/ConformanceTest.php b/tests/symfony/functional/MetadataService/ConformanceTest.php index 878dc156..d2fabd48 100644 --- a/tests/symfony/functional/MetadataService/ConformanceTest.php +++ b/tests/symfony/functional/MetadataService/ConformanceTest.php @@ -7,16 +7,13 @@ use PHPUnit\Framework\Attributes\Test; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\HttpClient\Response\MockResponse; -use Webauthn\AttestationStatement\AttestationStatement; -use Webauthn\AuthenticatorAttestationResponse; +use Symfony\Component\Serializer\SerializerInterface; use Webauthn\AuthenticatorAttestationResponseValidator; use Webauthn\Exception\AuthenticatorResponseVerificationException; +use Webauthn\PublicKeyCredential; use Webauthn\PublicKeyCredentialCreationOptions; -use Webauthn\PublicKeyCredentialDescriptor; -use Webauthn\PublicKeyCredentialLoader; use Webauthn\Tests\Bundle\Functional\MockClientCallback; use Webauthn\Tests\MockedRequestTrait; -use Webauthn\TrustPath\CertificateTrustPath; /** * @internal @@ -28,6 +25,10 @@ final class ConformanceTest extends KernelTestCase #[Test] public function theMetadataStatementIsMissing(): void { + // Given + /** @var SerializerInterface $serializer */ + $serializer = self::getContainer()->get('webauthn-serializer'); + $callback = self::getContainer()->get(MockClientCallback::class); $callback->addResponses([ 'GET-https://fidoalliance.co.nz/blob.jwt' => new MockResponse(trim( @@ -40,23 +41,26 @@ public function theMetadataStatementIsMissing(): void ); $options = '{"status":"ok","errorMessage":"","rp":{"name":"Webauthn Demo","id":"webauthn.spomky-labs.com"},"pubKeyCredParams":[{"type":"public-key","alg":-8},{"type":"public-key","alg":-7},{"type":"public-key","alg":-43},{"type":"public-key","alg":-35},{"type":"public-key","alg":-36},{"type":"public-key","alg":-257},{"type":"public-key","alg":-258},{"type":"public-key","alg":-259},{"type":"public-key","alg":-37},{"type":"public-key","alg":-38},{"type":"public-key","alg":-39}],"challenge":"625PSan72RKYZz_TA4aOLx7ohRptcGnJme6z4k4k6tU","attestation":"direct","user":{"name":"um-3Ch1opGUBzAgO0VE5","id":"OTk3OGFlYWItNmE5Yi00ZDNlLTk2ODMtOWM3MDViNWNiYjAw","displayName":"Christiana Muntz"},"authenticatorSelection":{"requireResidentKey":false,"userVerification":"preferred"},"timeout":60000}'; $result = '{"id":"8cQglUiAnc63hmOyudycW7xdaJoH-pPVlRyg5Xl7aYM","rawId":"8cQglUiAnc63hmOyudycW7xdaJoH+pPVlRyg5Xl7aYM","response":{"attestationObject":"o2NmbXRmcGFja2VkZ2F0dFN0bXSjY2FsZyZjc2lnWEgwRgIhALCSEVyBYqnkYZmp-ZGpnS3qgaYzEu-cOafnRqyFyvgVAiEAxjsiNt3SdvBc7Lwq9O2CSLRF6OQDDB0D1lBHNYiFV4FjeDVjgVkERTCCBEEwggIpoAMCAQICAQEwDQYJKoZIhvcNAQELBQAwgaExGDAWBgNVBAMMD0ZJRE8yIFRFU1QgUk9PVDExMC8GCSqGSIb3DQEJARYiY29uZm9ybWFuY2UtdG9vbHNAZmlkb2FsbGlhbmNlLm9yZzEWMBQGA1UECgwNRklETyBBbGxpYW5jZTEMMAoGA1UECwwDQ1dHMQswCQYDVQQGEwJVUzELMAkGA1UECAwCTVkxEjAQBgNVBAcMCVdha2VmaWVsZDAeFw0xODA1MjMxNDM5NDNaFw0yODA1MjAxNDM5NDNaMIHCMSMwIQYDVQQDDBpGSURPMiBCQVRDSCBLRVkgcHJpbWUyNTZ2MTExMC8GCSqGSIb3DQEJARYiY29uZm9ybWFuY2UtdG9vbHNAZmlkb2FsbGlhbmNlLm9yZzEWMBQGA1UECgwNRklETyBBbGxpYW5jZTEiMCAGA1UECwwZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1ZMRIwEAYDVQQHDAlXYWtlZmllbGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARPOl5eq2wfvK6X9t9kSZZ2LHvvcgBAKnbG8jD2VW8XqpmbWX_Ev1CKr46e8M0BP1q5vSeRS_CAQ3jLzLEbibVGoywwKjAJBgNVHRMEAjAAMB0GA1UdDgQWBBRWTffA-MZVtqEfbE0Z879B4v0BeTANBgkqhkiG9w0BAQsFAAOCAgEAh92gn_ggiQXRLUHOCaTp1HpcBhsOw8ZwTKJBxwYK8ycQ5_QRXBcMRi8axVexH2HoUDTg_u-DkvH2UYGYjX_RAxgGIh4dPgrKXwVndtMwiI5QnQwXMocKtzyyeuSQv6INwk_QCuJL5LOAyPtNUWMTb_UvCcdYWjtZYFOeYQSK9T_6dtWSp6XAhIT4wf3CBaxyai-YiRn3nfi154vUrqtuDh56eODK7-Iezg9npbucln3XxW_kRhtk2FERSBmBoo7IotPd8NGTATnwUvt16vw6x3mW2a6zZGOOeYCQmeXlfNza7fSff1BdFWR5f4cJ0gFAv297Tf5dGZQvZD3DcyQ9OJeJ3RQQ9inX0Nhxk1-6cm1i2e8h9gTN7otjqYmnGjs3ezhPdax2AdrmckO43YNuchfTPECPTRzP4rQo3QbwGLeEAk_HV-oJmYiBkdhf2F2QLMm7SdeqZ1Jjg1W1vNJT288vj1EGF-_aKXg_bujAaK86_YNPBJaW9Rdw4EnfFUi5bEdkD5ZSpeAHCQzCDn2RzkBjs2rTFe4qRFUWtC-RZ4wFqRx70jXLIw-ArpeetpjtzJSNqQsqPlEvpyMxuV2ZjnruA2_ysP3RDzqNs7R8JVNKiie0RAbG7et43ULZcC7oix8JKYsJ6wDmX8Gyy7vWM-LS9XiZUH37sEvwKJbM-xxoYXV0aERhdGFYpJYE6oKCTpikraFLRGLQ1zqOxGkTDakbGTB0WSKfdKNZQQAAAFpLRuvimGZCfrekq5JrKqEvACDxxCCVSICdzreGY7K53JxbvF1omgf6k9WVHKDleXtpg6UBAgMmIAEhWCCPnYovnhb8uFlbWx3AJ68r1nA-r3Rp77jZ7QrzCi6LfyJYIIIvljxQ2lTzNH1o8YxxCLkUXT7NZP9Jf4pKHSLTkhka","clientDataJSON":"eyJvcmlnaW4iOiJodHRwczovL3dlYmF1dGhuLnNwb21reS1sYWJzLmNvbSIsImNoYWxsZW5nZSI6IjYyNVBTYW43MlJLWVp6X1RBNGFPTHg3b2hScHRjR25KbWU2ejRrNGs2dFUiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0"},"type":"public-key"}'; - $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::createFromString($options); - $publicKeyCredential = self::$kernel->getContainer()->get(PublicKeyCredentialLoader::class)->load($result); - $descriptor = $publicKeyCredential->getPublicKeyCredentialDescriptor(); - $response = $publicKeyCredential->response; - static::assertSame(PublicKeyCredentialDescriptor::CREDENTIAL_TYPE_PUBLIC_KEY, $descriptor->type); - static::assertSame( - hex2bin('f1c4209548809dceb78663b2b9dc9c5bbc5d689a07fa93d5951ca0e5797b6983'), - $descriptor->id + $publicKeyCredentialCreationOptions = $serializer->deserialize( + $options, + PublicKeyCredentialCreationOptions::class, + 'json' ); - static::assertSame([], $descriptor->transports); - static::assertInstanceOf(AuthenticatorAttestationResponse::class, $response); - static::assertSame(AttestationStatement::TYPE_BASIC, $response->attestationObject ->attStmt ->type); - static::assertInstanceOf(CertificateTrustPath::class, $response->attestationObject ->attStmt ->trustPath); - self::$kernel->getContainer()->get(AuthenticatorAttestationResponseValidator::class)->check( + + $publicKeyCredential = $serializer->deserialize($result, PublicKeyCredential::class, 'json'); + $publicKeyCredential->getPublicKeyCredentialDescriptor(); + + // When + $pkSource = self::$kernel->getContainer()->get(AuthenticatorAttestationResponseValidator::class)->check( $publicKeyCredential->response, $publicKeyCredentialCreationOptions, 'webauthn.spomky-labs.com' ); + + // Then + static::assertSame( + hex2bin('f1c4209548809dceb78663b2b9dc9c5bbc5d689a07fa93d5951ca0e5797b6983'), + $pkSource->publicKeyCredentialId + ); } } diff --git a/tests/symfony/functional/SingleFileService.php b/tests/symfony/functional/SingleFileService.php index 7589be83..18385c5c 100644 --- a/tests/symfony/functional/SingleFileService.php +++ b/tests/symfony/functional/SingleFileService.php @@ -6,6 +6,7 @@ use InvalidArgumentException; use Symfony\Component\Finder\Finder; +use Symfony\Component\Serializer\SerializerInterface; use Webauthn\MetadataService\Service\MetadataService; use Webauthn\MetadataService\Statement\MetadataStatement; use function array_key_exists; @@ -15,10 +16,11 @@ final class SingleFileService implements MetadataService /** * @var array */ - private array $statements; + private array $statements = []; public function __construct( - private readonly string $rootPath + private readonly string $rootPath, + private readonly SerializerInterface $serializer, ) { } @@ -51,7 +53,7 @@ private function loadMDS(): void { foreach ($this->getFilenames() as $filename) { $data = trim(file_get_contents($filename)); - $mds = MetadataStatement::createFromString($data); + $mds = $this->serializer->deserialize($data, MetadataStatement::class, 'json'); if ($mds->aaguid === null) { continue; }