Skip to content

Commit

Permalink
feat: adopt webauthn-lib 4.8.0 (#477)
Browse files Browse the repository at this point in the history
  • Loading branch information
asbiin authored Feb 26, 2024
1 parent a3347bc commit 5b957cd
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 49 deletions.
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@
"require": {
"php": ">=8.1",
"illuminate/support": "^9.0 || ^10.0",
"phpdocumentor/reflection-docblock": "^5.3",
"psr/http-factory-implementation": "1.0",
"symfony/property-access": "^6.4 || ^7.0",
"symfony/property-info": "^6.4 || ^7.0",
"symfony/serializer": "^6.4 || ^7.0",
"web-auth/cose-lib": "^4.0",
"web-auth/webauthn-lib": "^4.7.1",
"web-auth/webauthn-lib": "^4.8.0",
"web-token/jwt-signature": "^3.0"
},
"conflict": {
Expand Down
11 changes: 6 additions & 5 deletions src/Services/Webauthn/CredentialAssertionValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@
use LaravelWebauthn\Exceptions\ResponseMismatchException;
use LaravelWebauthn\Services\Webauthn;
use ParagonIE\ConstantTime\Base64UrlSafe;
use Symfony\Component\Serializer\SerializerInterface;
use Webauthn\AuthenticatorAssertionResponse;
use Webauthn\AuthenticatorAssertionResponseValidator;
use Webauthn\PublicKeyCredential;
use Webauthn\PublicKeyCredentialLoader;
use Webauthn\PublicKeyCredentialRequestOptions;

class CredentialAssertionValidator extends CredentialValidator
{
public function __construct(
Request $request,
Cache $cache,
protected PublicKeyCredentialLoader $loader,
protected SerializerInterface $loader,
protected AuthenticatorAssertionResponseValidator $validator
) {
parent::__construct($request, $cache);
Expand All @@ -33,7 +33,8 @@ public function __construct(
public function __invoke(User $user, array $data): bool
{
// Load the data
$publicKeyCredential = $this->loader->loadArray($data);
$content = json_encode($data, flags: JSON_THROW_ON_ERROR);
$publicKeyCredential = $this->loader->deserialize($content, PublicKeyCredential::class, 'json');

// Check the response against the request
$this->validator->check(
Expand All @@ -53,9 +54,9 @@ public function __invoke(User $user, array $data): bool
protected function pullPublicKey(User $user): PublicKeyCredentialRequestOptions
{
try {
$value = json_decode($this->cache->pull($this->cacheKey($user)), true, flags: JSON_THROW_ON_ERROR);
$value = $this->cache->pull($this->cacheKey($user));

return PublicKeyCredentialRequestOptions::createFromArray($value);
return $this->loader->deserialize($value, PublicKeyCredentialRequestOptions::class, 'json');
} catch (\Exception $e) {
app('webauthn.log')->debug('Webauthn publickKey deserialize error', ['exception' => $e]);
abort(404);
Expand Down
11 changes: 6 additions & 5 deletions src/Services/Webauthn/CredentialAttestationValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@
use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Http\Request;
use LaravelWebauthn\Exceptions\ResponseMismatchException;
use Symfony\Component\Serializer\SerializerInterface;
use Webauthn\AuthenticatorAttestationResponse;
use Webauthn\AuthenticatorAttestationResponseValidator;
use Webauthn\PublicKeyCredential;
use Webauthn\PublicKeyCredentialCreationOptions;
use Webauthn\PublicKeyCredentialLoader;
use Webauthn\PublicKeyCredentialSource;

class CredentialAttestationValidator extends CredentialValidator
{
public function __construct(
Request $request,
Cache $cache,
protected PublicKeyCredentialLoader $loader,
protected SerializerInterface $loader,
protected AuthenticatorAttestationResponseValidator $validator
) {
parent::__construct($request, $cache);
Expand All @@ -32,7 +32,8 @@ public function __construct(
public function __invoke(User $user, array $data): PublicKeyCredentialSource
{
// Load the data
$publicKeyCredential = $this->loader->loadArray($data);
$content = json_encode($data, flags: JSON_THROW_ON_ERROR);
$publicKeyCredential = $this->loader->deserialize($content, PublicKeyCredential::class, 'json');

// Check the response against the request
return $this->validator->check(
Expand All @@ -48,9 +49,9 @@ public function __invoke(User $user, array $data): PublicKeyCredentialSource
protected function pullPublicKey(User $user): PublicKeyCredentialCreationOptions
{
try {
$value = json_decode($this->cache->pull($this->cacheKey($user)), true, flags: JSON_THROW_ON_ERROR);
$value = $this->cache->pull($this->cacheKey($user));

return PublicKeyCredentialCreationOptions::createFromArray($value);
return $this->loader->deserialize($value, PublicKeyCredentialCreationOptions::class, 'json');
} catch (\Exception $e) {
app('webauthn.log')->debug('Webauthn publicKey deserialize error', ['exception' => $e]);
abort(404);
Expand Down
31 changes: 18 additions & 13 deletions src/WebauthnServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
use Symfony\Component\Serializer\SerializerInterface;
use Webauthn\AttestationStatement\AndroidKeyAttestationStatementSupport;
use Webauthn\AttestationStatement\AndroidSafetyNetAttestationStatementSupport;
use Webauthn\AttestationStatement\AppleAttestationStatementSupport;
Expand All @@ -52,9 +53,9 @@
use Webauthn\AuthenticatorAssertionResponseValidator;
use Webauthn\AuthenticatorAttestationResponseValidator;
use Webauthn\AuthenticatorSelectionCriteria;
use Webauthn\CeremonyStep\CeremonyStepManagerFactory;
use Webauthn\Counter\CounterChecker;
use Webauthn\Counter\ThrowExceptionIfInvalid;
use Webauthn\PublicKeyCredentialLoader;
use Webauthn\PublicKeyCredentialRpEntity;

class WebauthnServiceProvider extends ServiceProvider
Expand Down Expand Up @@ -188,15 +189,24 @@ protected function bindWebAuthnPackage(): void
), fn (AuthenticatorAttestationResponseValidator $responseValidator) => $responseValidator->setLogger($app['webauthn.log'])
)
);
$this->app->bind(
CeremonyStepManagerFactory::class,
fn ($app) => tap(new CeremonyStepManagerFactory, function (CeremonyStepManagerFactory $factory) use ($app) {
$factory->setExtensionOutputCheckerHandler($app[ExtensionOutputCheckerHandler::class]);
$factory->setAlgorithmManager($app[CoseAlgorithmManager::class]);
$factory->setCounterChecker($app[CounterChecker::class]);
})
);
$this->app->bind(
AuthenticatorAssertionResponseValidator::class,
fn ($app) => tap((new AuthenticatorAssertionResponseValidator(
null,
null,
$app[ExtensionOutputCheckerHandler::class],
$app[CoseAlgorithmManager::class]
))
->setCounterChecker($app[CounterChecker::class]), fn (AuthenticatorAssertionResponseValidator $responseValidator) => $responseValidator->setLogger($app['webauthn.log'])
$app[CoseAlgorithmManager::class],
null,
($app[CeremonyStepManagerFactory::class])->requestCeremony()
)), fn (AuthenticatorAssertionResponseValidator $responseValidator) => $responseValidator->setLogger($app['webauthn.log'])
)
);
$this->app->bind(
Expand All @@ -207,7 +217,6 @@ protected function bindWebAuthnPackage(): void
$app['config']->get('webauthn.userless')
)
);

$this->app->bind(
PublicKeyCredentialRpEntity::class,
fn ($app) => new PublicKeyCredentialRpEntity(
Expand All @@ -216,14 +225,6 @@ protected function bindWebAuthnPackage(): void
$app['config']->get('webauthn.icon')
)
);
$this->app->bind(
PublicKeyCredentialLoader::class,
fn ($app) => tap(new PublicKeyCredentialLoader(
$app[AttestationObjectLoader::class]
), fn (PublicKeyCredentialLoader $loader) => $loader->setLogger($app['webauthn.log'])
)
);

$this->app->bind(
CoseAlgorithmManager::class,
fn ($app) => $app[CoseAlgorithmManagerFactory::class]
Expand Down Expand Up @@ -256,6 +257,10 @@ protected function bindWebAuthnPackage(): void
}
})
);
$this->app->bind(
SerializerInterface::class,
fn ($app) => (new \Webauthn\Denormalizer\WebauthnSerializerFactory($app[AttestationStatementSupportManager::class]))->create()
);
}

/**
Expand Down
55 changes: 30 additions & 25 deletions tests/Unit/Services/WebauthnTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use CBOR\ListObject;
use CBOR\MapItem;
use CBOR\MapObject;
use CBOR\NegativeIntegerObject;
use CBOR\TextStringObject;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Testing\DatabaseTransactions;
Expand Down Expand Up @@ -46,30 +47,30 @@ public function test_get_register_data()
/**
* @test
*/
// public function test_do_register_data()
// {
// $user = $this->signIn();

// $publicKey = $this->app[PrepareCreationData::class]($user);
// $this->assertInstanceOf(\Webauthn\PublicKeyCredentialCreationOptions::class, $publicKey);

// $data = $this->getAttestationData($publicKey);

// $this->app[ValidateKeyCreation::class]($user, $data, 'name');

// $this->assertDatabaseHas('webauthn_keys', [
// 'user_id' => $user->getAuthIdentifier(),
// 'name' => 'name',
// 'credentialId' => 'MA==',
// 'type' => 'public-key',
// 'transports' => '[]',
// 'attestationType' => 'none',
// 'trustPath' => '{"type":"Webauthn\\\\TrustPath\\\\EmptyTrustPath"}',
// 'aaguid' => '00000000-0000-0000-0000-000000000000',
// 'credentialPublicKey' => 'oWNrZXlldmFsdWU=',
// 'counter' => '1',
// ]);
// }
public function test_do_register_data()
{
$user = $this->signIn();

$publicKey = $this->app[PrepareCreationData::class]($user);
$this->assertInstanceOf(\Webauthn\PublicKeyCredentialCreationOptions::class, $publicKey);

$data = $this->getAttestationData($publicKey);

$this->app[ValidateKeyCreation::class]($user, $data, 'name');

$this->assertDatabaseHas('webauthn_keys', [
'user_id' => $user->getAuthIdentifier(),
'name' => 'name',
'credentialId' => 'MA==',
'type' => 'public-key',
'transports' => '[]',
'attestationType' => 'none',
'trustPath' => '{"type":"Webauthn\\\\TrustPath\\\\EmptyTrustPath"}',
'aaguid' => '30303030-3030-3030-3030-303030303030',
'credentialPublicKey' => 'omExZXZhbHVlYTMm',
'counter' => '1',
]);
}

/**
* @test
Expand Down Expand Up @@ -141,9 +142,13 @@ private function getAttestationData($publicKey)
pack('n', 1).'0'. // credentialLength
((string) new MapObject([
new MapItem(
new TextStringObject('key'),
new TextStringObject('1'),
new TextStringObject('value')
),
new MapItem(
new TextStringObject('3'),
new NegativeIntegerObject(6, null)
),
])) // credentialPublicKey
)
),
Expand Down

0 comments on commit 5b957cd

Please sign in to comment.