Skip to content

Commit a8d3e78

Browse files
committed
feat: add webauthn signal api
1 parent 15539f2 commit a8d3e78

9 files changed

+292
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace Webauthn\Denormalizer;
4+
5+
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
6+
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
7+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
8+
use Webauthn\PublicKeyCredentialDescriptor;
9+
use Webauthn\Signal\AllAcceptedCredentials;
10+
11+
class SignalAllAcceptedCredentialsDenormalizer implements NormalizerInterface, NormalizerAwareInterface
12+
{
13+
use NormalizerAwareTrait;
14+
15+
/**
16+
* @return array<class-string, bool>
17+
*/
18+
public function getSupportedTypes(?string $format): array
19+
{
20+
return [
21+
AllAcceptedCredentials::class => true,
22+
];
23+
}
24+
25+
/**
26+
* @return array<string, mixed>
27+
*/
28+
public function normalize(mixed $data, ?string $format = null, array $context = []): array
29+
{
30+
assert($data instanceof AllAcceptedCredentials);
31+
32+
$normalized_rp = $this->normalizer->normalize(
33+
$data->rp,
34+
$format,
35+
$context
36+
);
37+
38+
$normalized_user = $this->normalizer->normalize(
39+
$data->user,
40+
$format,
41+
$context
42+
);
43+
44+
$normalized_credentials = array_map(function (PublicKeyCredentialDescriptor $credential) use ($format, $context) {
45+
return $this->normalizer->normalize($credential, $format, $context);
46+
}, $data->allAcceptedCredentials);
47+
48+
return [
49+
'rpId' => $normalized_rp['id'],
50+
'userId' => $normalized_user['id'],
51+
'allAcceptedCredentialIds' => array_map(
52+
fn (array $credential): string => $credential['id'],
53+
$normalized_credentials
54+
),
55+
];
56+
}
57+
58+
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
59+
{
60+
return $data instanceof AllAcceptedCredentials;
61+
}
62+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace Webauthn\Denormalizer;
4+
5+
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
6+
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
7+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
8+
use Webauthn\Signal\CurrentUserDetails;
9+
10+
class SignalCurrentUserDetailsDenormalizer implements NormalizerInterface, NormalizerAwareInterface
11+
{
12+
use NormalizerAwareTrait;
13+
14+
/**
15+
* @return array<class-string, bool>
16+
*/
17+
public function getSupportedTypes(?string $format): array
18+
{
19+
return [
20+
CurrentUserDetails::class => true,
21+
];
22+
}
23+
24+
/**
25+
* @return array<string, mixed>
26+
*/
27+
public function normalize(mixed $data, ?string $format = null, array $context = []): array
28+
{
29+
assert($data instanceof CurrentUserDetails);
30+
31+
$normalized_rp = $this->normalizer->normalize(
32+
$data->rp,
33+
$format,
34+
$context
35+
);
36+
37+
$normalized_user = $this->normalizer->normalize(
38+
$data->user,
39+
$format,
40+
$context
41+
);
42+
43+
return [
44+
'rpId' => $normalized_rp['id'],
45+
'userId' => $normalized_user['id'],
46+
'name' => $normalized_user['name'],
47+
'displayName' => $normalized_user['displayName'],
48+
];
49+
}
50+
51+
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
52+
{
53+
return $data instanceof CurrentUserDetails;
54+
}
55+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace Webauthn\Denormalizer;
4+
5+
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
6+
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
7+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
8+
use Webauthn\Signal\UnknownCredential;
9+
10+
class SignalUnknownCredentialDenormalizer implements NormalizerInterface, NormalizerAwareInterface
11+
{
12+
use NormalizerAwareTrait;
13+
14+
/**
15+
* @return array<class-string, bool>
16+
*/
17+
public function getSupportedTypes(?string $format): array
18+
{
19+
return [
20+
UnknownCredential::class => true,
21+
];
22+
}
23+
24+
/**
25+
* @return array<string, mixed>
26+
*/
27+
public function normalize(mixed $data, ?string $format = null, array $context = []): array
28+
{
29+
assert($data instanceof UnknownCredential);
30+
31+
$normalized_rp = $this->normalizer->normalize(
32+
$data->rp,
33+
$format,
34+
$context
35+
);
36+
37+
$normalized_credential = $this->normalizer->normalize(
38+
$data->credential,
39+
$format,
40+
$context
41+
);
42+
43+
return [
44+
'rpId' => $normalized_rp['id'],
45+
'credentialId' => $normalized_credential['id'],
46+
];
47+
}
48+
49+
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
50+
{
51+
return $data instanceof UnknownCredential;
52+
}
53+
}

src/webauthn/src/Denormalizer/WebauthnSerializerFactory.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ public function create(): SerializerInterface
6060
new PublicKeyCredentialOptionsDenormalizer(),
6161
new PublicKeyCredentialSourceDenormalizer(),
6262
new PublicKeyCredentialUserEntityDenormalizer(),
63+
new SignalAllAcceptedCredentialsDenormalizer(),
64+
new SignalCurrentUserDetailsDenormalizer(),
65+
new SignalUnknownCredentialDenormalizer(),
6366
new TrustPathDenormalizer(),
6467
new UidNormalizer(),
6568
new ArrayDenormalizer(),
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Webauthn\Signal;
4+
5+
use Webauthn\PublicKeyCredentialDescriptor;
6+
use Webauthn\PublicKeyCredentialRpEntity;
7+
use Webauthn\PublicKeyCredentialUserEntity;
8+
9+
readonly class AllAcceptedCredentials implements Signal
10+
{
11+
/**
12+
* @param PublicKeyCredentialDescriptor[] $allAcceptedCredentials
13+
*/
14+
public function __construct(
15+
public PublicKeyCredentialRpEntity $rp,
16+
public PublicKeyCredentialUserEntity $user,
17+
public array $allAcceptedCredentials,
18+
) {}
19+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Webauthn\Signal;
4+
5+
use Webauthn\PublicKeyCredentialRpEntity;
6+
use Webauthn\PublicKeyCredentialUserEntity;
7+
8+
readonly class CurrentUserDetails implements Signal
9+
{
10+
public function __construct(
11+
public PublicKeyCredentialRpEntity $rp,
12+
public PublicKeyCredentialUserEntity $user,
13+
) {}
14+
}

src/webauthn/src/Signal/Signal.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Webauthn\Signal;
6+
7+
interface Signal
8+
{
9+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Webauthn\Signal;
4+
5+
use Webauthn\PublicKeyCredentialDescriptor;
6+
use Webauthn\PublicKeyCredentialRpEntity;
7+
8+
readonly class UnknownCredential implements Signal
9+
{
10+
public function __construct(
11+
public PublicKeyCredentialRpEntity $rp,
12+
public PublicKeyCredentialDescriptor $credential,
13+
) {}
14+
}

tests/library/Unit/SerializerTest.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@
1010
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
1111
use Webauthn\AuthenticatorSelectionCriteria;
1212
use Webauthn\PublicKeyCredentialCreationOptions;
13+
use Webauthn\PublicKeyCredentialDescriptor;
1314
use Webauthn\PublicKeyCredentialParameters;
1415
use Webauthn\PublicKeyCredentialRpEntity;
1516
use Webauthn\PublicKeyCredentialUserEntity;
17+
use Webauthn\Signal\AllAcceptedCredentials;
18+
use Webauthn\Signal\CurrentUserDetails;
19+
use Webauthn\Signal\UnknownCredential;
1620
use Webauthn\Tests\AbstractTestCase;
1721
use Webauthn\TrustPath\CertificateTrustPath;
1822
use Webauthn\TrustPath\EmptyTrustPath;
@@ -134,4 +138,63 @@ public function theCredentialCanBeDeserialized(): void
134138
$json,
135139
);
136140
}
141+
142+
#[Test]
143+
public function itSerializesSignalUnknownCredential(): void
144+
{
145+
$rp = new PublicKeyCredentialRpEntity('Example.com', 'rp.example.com');
146+
$credential = new PublicKeyCredentialDescriptor(
147+
PublicKeyCredentialDescriptor::CREDENTIAL_TYPE_PUBLIC_KEY,
148+
'cred-123',
149+
[]
150+
);
151+
$signal = new UnknownCredential($rp, $credential);
152+
153+
$serializer = $this->getSerializer();
154+
$json = $serializer->serialize($signal, 'json');
155+
static::assertJsonStringEqualsJsonString(
156+
'{"rpId":"rp.example.com","credentialId":"Y3JlZC0xMjM"}',
157+
$json
158+
);
159+
}
160+
161+
#[Test]
162+
public function itSerializesSignalAllAcceptedCredentials(): void
163+
{
164+
$rp = new PublicKeyCredentialRpEntity('Example.com', 'rp.example.com');
165+
$user = new PublicKeyCredentialUserEntity('john.doe', 'user-1', 'John Doe');
166+
$cred1 = new PublicKeyCredentialDescriptor(
167+
PublicKeyCredentialDescriptor::CREDENTIAL_TYPE_PUBLIC_KEY,
168+
'cred-1',
169+
[]
170+
);
171+
$cred2 = new PublicKeyCredentialDescriptor(
172+
PublicKeyCredentialDescriptor::CREDENTIAL_TYPE_PUBLIC_KEY,
173+
'cred-2',
174+
[]
175+
);
176+
$signal = new AllAcceptedCredentials($rp, $user, [$cred1, $cred2]);
177+
178+
$serializer = $this->getSerializer();
179+
$json = $serializer->serialize($signal, 'json');
180+
static::assertJsonStringEqualsJsonString(
181+
'{"rpId":"rp.example.com","userId":"dXNlci0x","allAcceptedCredentialIds":["Y3JlZC0x","Y3JlZC0y"]}',
182+
$json
183+
);
184+
}
185+
186+
#[Test]
187+
public function itSerializesSignalCurrentUserDetails(): void
188+
{
189+
$rp = new PublicKeyCredentialRpEntity('Example.com', 'rp.example.com');
190+
$user = new PublicKeyCredentialUserEntity('john.doe', 'user-1', 'John Doe');
191+
$signal = new CurrentUserDetails($rp, $user);
192+
193+
$serializer = $this->getSerializer();
194+
$json = $serializer->serialize($signal, 'json');
195+
static::assertJsonStringEqualsJsonString(
196+
'{"rpId":"rp.example.com","userId":"dXNlci0x","name":"john.doe","displayName":"John Doe"}',
197+
$json
198+
);
199+
}
137200
}

0 commit comments

Comments
 (0)