Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve userless connection mode #488

Merged
merged 3 commits into from
Jul 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Events/WebauthnLoginData.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class WebauthnLoginData
* @param PublicKeyCredentialRequestOptions $publicKey The authentication data.
*/
public function __construct(
public User $user,
public ?User $user,
public PublicKeyCredentialRequestOptions $publicKey
) {}
}
2 changes: 1 addition & 1 deletion src/Services/Webauthn.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public static function forgetAuthenticate(): void
/**
* Get publicKey data to prepare Webauthn login.
*/
public static function prepareAssertion(User $user): PublicKeyCredentialRequestOptions
public static function prepareAssertion(?User $user): PublicKeyCredentialRequestOptions
{
return tap(app(RequestOptionsFactory::class)($user), function ($publicKey) use ($user) {
WebauthnLoginData::dispatch($user, $publicKey);
Expand Down
18 changes: 12 additions & 6 deletions src/Services/Webauthn/CredentialAssertionValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function __construct(
*
* @throws ResponseMismatchException
*/
public function __invoke(User $user, array $data): bool
public function __invoke(?User $user, array $data): bool
{
// Load the data
$content = json_encode($data, flags: JSON_THROW_ON_ERROR);
Expand All @@ -51,11 +51,15 @@ public function __invoke(User $user, array $data): bool
/**
* Get public Key credential.
*/
protected function pullPublicKey(User $user): PublicKeyCredentialRequestOptions
protected function pullPublicKey(?User $user): PublicKeyCredentialRequestOptions
{
try {
$value = $this->cache->pull($this->cacheKey($user));

if ($value === null && in_array(config('webauthn.userless'), ['required', 'preferred'], true)) {
$value = $this->cache->pull($this->cacheKey(null));
}

return $this->loader->deserialize($value, PublicKeyCredentialRequestOptions::class, 'json');
} catch (\Exception $e) {
app('webauthn.log')->debug('Webauthn publickKey deserialize error', ['exception' => $e]);
Expand All @@ -79,14 +83,16 @@ protected function getResponse(PublicKeyCredential $publicKeyCredential): Authen
/**
* Get credential source from user and public key.
*/
protected function getCredentialSource(User $user, PublicKeyCredential $publicKeyCredential)
protected function getCredentialSource(?User $user, PublicKeyCredential $publicKeyCredential)
{
$credentialId = $publicKeyCredential->rawId;

return (Webauthn::model())::where('user_id', $user->getAuthIdentifier())
->where(fn ($query) => $query->where('credentialId', Base64UrlSafe::encode($credentialId))
return (Webauthn::model())::where(
fn ($query) => $query->where('credentialId', Base64UrlSafe::encode($credentialId))
->orWhere('credentialId', Base64UrlSafe::encodeUnpadded($credentialId))
)
)->where(
fn ($query) => $user !== null ? $query->where('user_id', $user->getAuthIdentifier()) : $query
)
->firstOrFail()
->publicKeyCredentialSource;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Services/Webauthn/CredentialValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ public function __construct(
/**
* Returns the cache key to remember the challenge for the user.
*/
protected function cacheKey(User $user): string
protected function cacheKey(?User $user): string
{
return implode(
'|',
[
self::CACHE_PUBLICKEY_REQUEST,
get_class($user).':'.$user->getAuthIdentifier(),
$user !== null ? get_class($user).':'.$user->getAuthIdentifier() : '',
hash('sha512', $this->request->host().'|'.$this->request->ip()),
]
);
Expand Down
6 changes: 3 additions & 3 deletions src/Services/Webauthn/RequestOptionsFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function __construct(
/**
* Create a new PublicKeyCredentialCreationOptions object.
*/
public function __invoke(User $user): PublicKeyCredentialRequestOptions
public function __invoke(?User $user): PublicKeyCredentialRequestOptions
{
$publicKey = new PublicKeyCredentialRequestOptions(
$this->getChallenge(),
Expand Down Expand Up @@ -63,9 +63,9 @@ private static function getUserVerification(Config $config): ?string
*
* @return array<array-key,PublicKeyCredentialDescriptor>
*/
private function getAllowedCredentials(User $user): array
private function getAllowedCredentials(?User $user): array
{
return CredentialRepository::getRegisteredKeys($user);
return $user !== null ? CredentialRepository::getRegisteredKeys($user) : [];
}

/**
Expand Down