Skip to content

Commit

Permalink
[3.x] FIX: userHandle compatibility between webauthn.js and Webpass (#94
Browse files Browse the repository at this point in the history
)
  • Loading branch information
Tugzrida authored Aug 14, 2024
1 parent 208df5d commit 7a0c742
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 5 deletions.
13 changes: 10 additions & 3 deletions src/Assertion/Validator/Pipes/CheckCredentialIsForUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Closure;
use Laragear\WebAuthn\Assertion\Validator\AssertionValidation;
use Laragear\WebAuthn\ByteBuffer;
use Laragear\WebAuthn\Exceptions\AssertionException;
use Ramsey\Uuid\Exception\InvalidUuidStringException;
use Ramsey\Uuid\Uuid;
Expand Down Expand Up @@ -67,12 +68,18 @@ protected function validateId(AssertionValidation $validation): void
// of the authenticator, which is pushed from the application to be saved. If
// the userHandle cannot be decoded and normalized, then surely is invalid.
try {
$handle = Uuid::fromString($validation->json->get('response.userHandle'))->getHex()->toString();
$handle = Uuid::fromString($validation->json->get('response.userHandle'));
} catch (InvalidUuidStringException) {
throw AssertionException::make('The userHandle is not a valid hexadecimal UUID (32/36 characters).');
try {
// This is required for compatibility with credentials created by versions
// of Webpass that used SimpleWebAuthn/browser < v10.0.0
$handle = Uuid::fromString(ByteBuffer::decodeBase64Url($validation->json->get('response.userHandle')));
} catch (InvalidUuidStringException) {
throw AssertionException::make('The userHandle is not a valid hexadecimal UUID (32/36 characters).');
}
}

if (! hash_equals(Uuid::fromString($validation->credential->user_id)->getHex()->toString(), $handle)) {
if (! hash_equals(Uuid::fromString($validation->credential->user_id)->getHex()->toString(), $handle->getHex()->toString())) {
throw AssertionException::make('User ID is not owner of the stored credential.');
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/Attestation/Creator/Pipes/AddUserDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Closure;
use Laragear\WebAuthn\Attestation\Creator\AttestationCreation;
use Ramsey\Uuid\Uuid;

/**
* @internal
Expand All @@ -19,7 +20,7 @@ public function handle(AttestationCreation $attestable, Closure $next): mixed
$existingId = $attestable->user->webAuthnCredentials()->getQuery()->value('user_id');

$attestable->json->set('user', [
'id' => $existingId ?: $attestable->user->webAuthnId()->getHex()->toString(),
'id' => ($existingId ? Uuid::fromString($existingId) : $attestable->user->webAuthnId())->getHex()->toString(),
...$attestable->user->webAuthnData(),
]);

Expand Down
13 changes: 13 additions & 0 deletions tests/Assertion/ValidationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,19 @@ public function test_credential_check_is_malformed_user_handle(): void
$this->validator->send($this->validation)->thenReturn();
}

public function test_credential_check_base64_user_handle(): void
{
$assertionResponse = FakeAuthenticator::assertionResponse();

$assertionResponse['response']['userHandle'] = base64_encode($assertionResponse['response']['userHandle']);

$this->validation->json = new JsonTransport($assertionResponse);

$this->validation->user = WebAuthnAuthenticatableUser::query()->first();

static::assertInstanceOf(AssertionValidation::class, $this->validator->send($this->validation)->thenReturn());
}

public function test_credential_check_is_not_for_user_id(): void
{
DB::table('webauthn_credentials')->where('id', FakeAuthenticator::CREDENTIAL_ID)->update([
Expand Down
2 changes: 1 addition & 1 deletion tests/Attestation/CreatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ public function test_user_reuses_uuid_from_other_credential(): void
])->save();

$this->response()
->assertJsonPath('user.id', $uuid->toString());
->assertJsonPath('user.id', $uuid->getHex()->toString());
}

public function test_adds_existing_credentials_if_unique_by_default(): void
Expand Down

0 comments on commit 7a0c742

Please sign in to comment.