Giter Club home page Giter Club logo

larapass's Introduction

This package has been superseded by Laragear WebAuthn.


Lukenn Sabellano - Unsplash (UL) #RDufjtg6JpQ

Latest Stable Version License Coverage Status Laravel Octane Compatible

Larapass

Authenticate users with just their device, fingerprint or biometric data. Goodbye passwords!

This enables WebAuthn authentication inside Laravel authentication driver, and comes with everything but the kitchen sink.

Requisites

  • PHP 7.4 or PHP 8.0
  • Laravel 7.18 (July 2020) or Laravel 8.x

For Laravel 9.x supports and onwards, use Laragear WebAuthn.

Installation

Just hit the console and require it with Composer.

composer require darkghosthunter/larapass

Unfortunately, using WebAuthn is not a "walk in the park", this package allows you to enable WebAuthn in the most easiest way possible.

Table of contents

What is WebAuthn? How it uses fingerprints or else?

In a nutshell, major browsers are compatible with Web Authentication API, pushing authentication to the device (fingerprints, Face ID, patterns, codes, etc) instead of plain-text passwords.

This package validates the WebAuthn payload from the devices using a custom user provider.

If you have any doubts about WebAuthn, check this small FAQ. For a more deep dive, check WebAuthn.io, WebAuthn.me and Google WebAuthn tutorial.

Set up

We need to make sure your users can register their devices and authenticate with them.

  1. Add the eloquent-webauthn driver.
  2. Create the webauthn_credentials table.
  3. Implement the contract and trait

After that, you can quickly start WebAuthn with the included controllers and helpers to make your life easier.

  1. Register the routes
  2. Use the Javascript helper
  3. Set up account recovery

1. Add the eloquent-webauthn driver

This package comes with an Eloquent-compatible user provider that validates WebAuthn responses from the devices.

Go to your config/auth.php configuration file, and change the driver of the provider you're using to eloquent-webauthn.

return [
    // ...

    'providers' => [
        'users' => [
            // 'driver' => 'eloquent', // Default Eloquent User Provider 
            'driver' => 'eloquent-webauthn',
            'model' => App\User::class,
        ],
    ]
];

If you plan to create your own user provider driver for WebAuthn, remember to inject the WebAuthnAssertValidator to properly validate the user with the incoming response.

2. Create the webauthn_credentials table

Create the webauthn_credentials table by publishing the migration files and migrating the table:

php artisan vendor:publish --provider="DarkGhostHunter\Larapass\LarapassServiceProvider" --tag="migrations"
php artisan migrate

3. Implement the contract and trait

Add the WebAuthnAuthenticatable contract and the WebAuthnAuthentication trait to the Authenticatable user class, or any that uses authentication.

<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;
use DarkGhostHunter\Larapass\Contracts\WebAuthnAuthenticatable;
use DarkGhostHunter\Larapass\WebAuthnAuthentication;

class User extends Authenticatable implements WebAuthnAuthenticatable
{
    use WebAuthnAuthentication;

    // ...
}

The trait is used to tie the User model to the WebAuthn data contained in the database.

4. Register the routes (optional)

Finally, you will need to add the routes for registering and authenticating users. If you want a quick start, just publish the controllers included in Larapass.

php artisan vendor:publish --provider="DarkGhostHunter\Larapass\LarapassServiceProvider" --tag="controllers"

You can copy-paste these route definitions in your routes/web.php file.

use App\Http\Controllers\Auth\WebAuthnRegisterController;
use App\Http\Controllers\Auth\WebAuthnLoginController;

Route::post('webauthn/register/options', [WebAuthnRegisterController::class, 'options'])
     ->name('webauthn.register.options');
Route::post('webauthn/register', [WebAuthnRegisterController::class, 'register'])
     ->name('webauthn.register');

Route::post('webauthn/login/options', [WebAuthnLoginController::class, 'options'])
     ->name('webauthn.login.options');
Route::post('webauthn/login', [WebAuthnLoginController::class, 'login'])
     ->name('webauthn.login');

In your frontend scripts, point the requests to these routes.

If you want full control, you can opt-out of these helper controllers and use your own logic. Use the AttestWebAuthn and AssertsWebAuthn traits if you need to start with something.

5. Use the Javascript helper (optional)

This package includes a convenient script to handle registration and login via WebAuthn. To use it, just publish the larapass.js asset into your application public resources.

php artisan vendor:publish --provider="DarkGhostHunter\Larapass\LarapassServiceProvider" --tag="public"

You will receive the vendor/larapass/js/larapass.js file which you can include into your authentication views and use it programmatically, anyway you want.

<script src="{{ asset('vendor/larapass/js/larapass.js') }}"></script>

<!-- Registering credentials -->
<script>
    const register = (event) => {
        event.preventDefault()
        new Larapass({
            register: 'webauthn/register',
            registerOptions: 'webauthn/register/options'
        }).register()
          .then(response => alert('Registration successful!'))
          .catch(response => alert('Something went wrong, try again!'))
    }

    document.getElementById('register-form').addEventListener('submit', register)
</script>

<!-- Login users -->
<script>
    const login = (event) => {
        event.preventDefault()
        new Larapass({
            login: 'webauthn/login',
            loginOptions: 'webauthn/login/options'
        }).login({
            email: document.getElementById('email').value
        }).then(response => alert('Authentication successful!'))
          .catch(error => alert('Something went wrong, try again!'))
    }

    document.getElementById('login-form').addEventListener('submit', login)
</script>

You can bypass the route list declaration if you're using the defaults. The example above includes them just for show. Be sure to create modify this script for your needs.

Also, the helper allows headers on the action request, on both registration and login.

new Larapass({
    login: 'webauthn/login',
    loginOptions: 'webauthn/login/options'
}).login({
    email: document.getElementById('email').value,
}, {
    'My-Custom-Header': 'This is sent with the signed challenge',
})

You can copy-paste it and import into a transpiler like Laravel Mix, Babel or Webpack. If the script doesn't suit your needs, you're free to create your own.

Remembering Users

You can enable it by just issuing the WebAuthn-Remember header value to true when pushing the signed login challenge from your frontend. We can do this easily with the included Javascript helper.

new Larapass.login({
    email: document.getElementById('email').value
}, {
    'WebAuthn-Remember': true
})

Alternatively, you can add the remember key to the outgoing JSON Payload if you're using your own scripts. Both ways are accepted.

You can override this behaviour in the AssertsWebAuthn trait.

6. Set up account recovery (optional)

Probably you will want to offer a way to "recover" an account if the user loses his credentials, which is basically a way to attach a new one. You can use controllers which are also published, along with these routes:

use App\Http\Controllers\Auth\WebAuthnDeviceLostController;
use App\Http\Controllers\Auth\WebAuthnRecoveryController;

Route::get('webauthn/lost', [WebAuthnDeviceLostController::class, 'showDeviceLostForm'])
     ->name('webauthn.lost.form');
Route::post('webauthn/lost', [WebAuthnDeviceLostController::class, 'sendRecoveryEmail'])
     ->name('webauthn.lost.send');

Route::get('webauthn/recover', [WebAuthnRecoveryController::class, 'showResetForm'])
     ->name('webauthn.recover.form');
Route::post('webauthn/recover/options', [WebAuthnRecoveryController::class, 'options'])
     ->name('webauthn.recover.options');
Route::post('webauthn/recover/register', [WebAuthnRecoveryController::class, 'recover'])
     ->name('webauthn.recover');

These come with new views and translation lines, so you can override them if you're not happy with what is included.

You can also override the views in resources/vendor/larapass and the notification being sent using the sendCredentialRecoveryNotification method of the user.

After that, don't forget to add a new token broker in your config/auth.php. We will need it to store the tokens from the recovery procedure.

return [
    // ...

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],

        // New for WebAuthn
        'webauthn' => [
            'provider' => 'users', // The user provider using WebAuthn.
            'table' => 'web_authn_recoveries', // The table to store the recoveries.
            'expire' => 60,
            'throttle' => 60,
        ],
    ],
];

Confirmation middleware

Following the same principle of the password.confirm middleware, Larapass includes a the webauthn.confirm middleware that will ask the user to confirm with his device before entering a given route.

Route::get('this/is/important', function () {
    return 'This is very important!';
})->middleware('webauthn.confirm');

When publishing the controllers, the WebAuthnConfirmController will be in your controller files ready to accept confirmations. You just need to register the route by just copy-pasting these:

Route::get('webauthn/confirm', 'Auth\WebAuthnConfirmController@showConfirmForm')
     ->name('webauthn.confirm.form');
Route::post('webauthn/confirm/options', 'Auth\WebAuthnConfirmController@options')
     ->name('webauthn.confirm.options');
Route::post('webauthn/confirm', 'Auth\WebAuthnConfirmController@confirm')
     ->name('webauthn.confirm');

As always, you can opt-out with your own logic. For these case take a look into the ConfirmsWebAuthn trait to start.

You can change how much time to remember the confirmation in the configuration.

Events

Since all authentication is handled by Laravel itself, the only event included is AttestationSuccessful, which fires when the registration is successful. It includes the user with the credentials persisted.

You can use this event to, for example, notify the user a new device has been registered. For that, you can use a listener.

public function handle(AttestationSuccessful $event)
{
    $event->user->notify(
        new DeviceRegisteredNotification($event->credential->getId())
    );
}

Operations with WebAuthn

This package simplifies operating with the WebAuthn ceremonies (attestation and assertion). For this, use the convenient WebAuthn facade.

Attestation (Register)

Use the generateAttestation and validateAttestation for your user. The latter returns the credentials validated, so you can save them manually.

<?php

use App\User; 
use Illuminate\Support\Facades\Auth;
use DarkGhostHunter\Larapass\Facades\WebAuthn;

$user = Auth::user();

// Create an attestation for a given user.
return WebAuthn::generateAttestation($user);

Then later we can verify it:

<?php

use App\User; 
use Illuminate\Support\Facades\Auth;
use DarkGhostHunter\Larapass\Facades\WebAuthn;

$user = Auth::user();

// Verify it
$credential = WebAuthn::validateAttestation(
    request()->json()->all(), $user
);

// And save it.
if ($credential) {
    $user->addCredential($credential);
} else {
    return 'Something went wrong with your device!';
}

Assertion (Login)

For assertion, simply create a request using generateAssertion and validate it with validateAssertion.

<?php

use App\User; 
use DarkGhostHunter\Larapass\Facades\WebAuthn;

// Find the user to assert, if there is any
$user = User::where('email', request()->input('email'))->first();

// Create an assertion for the given user (or a blank one if not found);
return WebAuthn::generateAssertion($user);

Then later we can verify it:

<?php

use App\User;
use Illuminate\Support\Facades\Auth;
use DarkGhostHunter\Larapass\Facades\WebAuthn;

// Verify the incoming assertion.
$credentials = WebAuthn::validateAssertion(
    request()->json()->all()
);

// If is valid, login the user of the credentials.
if ($credentials) {
    Auth::login(
        User::getFromCredentialId($credentials->getPublicKeyCredentialId())
    );
}

Credentials

You can manage the user credentials thanks to the WebAuthnAuthenticatable contract directly from within the user instance. The most useful methods are:

  • hasCredential(): Checks if the user has a given Credential ID.
  • addCredential(): Adds a new Credential Source.
  • removeCredential(): Removes an existing Credential by its ID.
  • flushCredentials(): Removes all credentials. You can exclude credentials by their id.
  • enableCredential(): Includes an existing Credential ID from authentication.
  • disableCredential(): Excludes an existing Credential ID from authentication.
  • getFromCredentialId(): Returns the user using the given Credential ID, if any.

You can use these methods to, for example, blacklist a stolen device/credential and register a new one, or disable WebAuthn completely by flushing all registered devices.

Advanced Configuration

Larapass was made to work out-of-the-box, but you can override the configuration by simply publishing the config file.

php artisan vendor:publish --provider="DarkGhostHunter\Larapass\LarapassServiceProvider" --tag="config"

After that, you will receive the config/larapass.php config file with an array like this:

<?php

return [
    'relaying_party' => [
        'name' => env('WEBAUTHN_NAME', env('APP_NAME')),
        'id'   => env('WEBAUTHN_ID'),
        'icon' => env('WEBAUTHN_ICON'),
    ],
    'bytes' => 16,
    'timeout' => 60,
    'cache' => env('WEBAUTHN_CACHE'),
    'algorithms' => [
        \Cose\Algorithm\Signature\ECDSA\ES256::class,
        \Cose\Algorithm\Signature\EdDSA\Ed25519::class,
        \Cose\Algorithm\Signature\ECDSA\ES384::class,
        \Cose\Algorithm\Signature\ECDSA\ES512::class,
        \Cose\Algorithm\Signature\RSA\RS256::class,
    ],
    'attachment' => null,
    'conveyance' => 'none',
    'login_verify' => 'preferred',
    'userless' => null,
    'unique' => false,
    'fallback' => true,
    'confirm_timeout' => 10800,
];

Relaying Party Information

return [
    'relaying_party' => [
        'name' => env('WEBAUTHN_NAME', env('APP_NAME')),
        'id'   => env('WEBAUTHN_ID'),
        'icon' => env('WEBAUTHN_ICON'),
    ],
];

The Relaying Party is just a way to uniquely identify your application in the user device:

  • name: The name of the application. Defaults to the application name.
  • id: Optional domain of the application. If null, the device will fill it internally.
  • icon: Optional image data in BASE64 (128 bytes maximum) or an image url.

Consider using the base domain like myapp.com as id to allow all the credential on subdomains like foo.myapp.com.

Challenge configuration

return [
    'bytes' => 16,
    'timeout' => 60,
    'cache' => env('WEBAUTHN_CACHE'),
];

The outgoing challenge to be signed is a random string of bytes. This controls how many bytes, the timeout of the challenge (which after is marked as invalid), and the cache used to store the challenge while its being resolved by the device.

Algorithms

return [
    'algorithms' => [
        \Cose\Algorithm\Signature\ECDSA\ES256::class,   // ECDSA with SHA-256
        \Cose\Algorithm\Signature\EdDSA\Ed25519::class, // EdDSA
        \Cose\Algorithm\Signature\ECDSA\ES384::class,   // ECDSA with SHA-384
        \Cose\Algorithm\Signature\ECDSA\ES512::class,   // ECDSA with SHA-512
        \Cose\Algorithm\Signature\RSA\RS256::class,     // RSASSA-PKCS1-v1_5 with SHA-256
    ],
];

This controls how the authenticator (device) will operate to create the public-private keys. These COSE Algorithms are the most compatible ones for in-device and roaming keys, since some must be transmitted on low bandwidth protocols.

Add or remove the classes unless you don't know what you're doing. Really. Just leave them as they are.

Key Attachment

return [
     'attachment' => null,
];

By default, the user decides what to use for registration. If you wish to exclusively use a cross-platform authentication (like USB Keys, CA Servers or Certificates) set this to true, or false if you want to enforce device-only authentication.

Attestation conveyance

return [
    'conveyance' => null,
];

Attestation Conveyance represents if the device key should be verified by you or not. While most of the time is not needed, you can change this to indirect (you verify it comes from a trustful source) or direct (the device includes validation data).

Leave as it if you don't know what you're doing.

Login verification

return [
    'login_verify' => 'preferred',
];

By default, most authenticators will require the user verification when login in. You can override this and set it as required if you want no exceptions.

You can also use discouraged to only check for user presence (like a "Continue" button), which may make the login faster but making it slightly less secure.

When setting userless as preferred or required will override this to required automatically.

Userless login (One touch, Typeless)

return [
    'userless' => null,
];

You can activate userless login, also known as one-touch login or typless login, for devices when they're being registered. You should change this to preferred in that case, since not all devices support the feature.

If this is activated (not null or discouraged), login verification will be mandatory.

This doesn't affect the login procedure, only the attestation (registration).

Unique

return [
    'unique' => false,
];

If true, the device will limit the creation of only one credential by device. This is done by telling the device the list of credentials ID the user already has. If at least one if already present in the device, the latter will return an error.

Password Fallback

return [
    'fallback' => true,
];

By default, this package allows to re-use the same eloquent-webauthn driver to log in users with passwords when the credentials are not a WebAuthn JSON payload.

Disabling the fallback will only validate the WebAuthn credentials. To handle classic user/password scenarios, you may create a separate guard.

Confirmation timeout

return [
    'confirm_timeout' => 10800,
];

When using the Confirmation middleware, the confirmation will be remembered for a set amount of seconds. By default, is 3 hours, which is enough for most scenarios.

Attestation and Metadata statements support

If you need very-high-level of security, you should use attestation and metadata statements. You will basically ask the authenticator for its authenticity and check it in a lot of ways.

For that, check this article and extend the classes in the Service Container as you need:

<?php

use Webauthn\AttestationStatement\AttestationStatementSupport;
use Webauthn\AttestationStatement\AndroidSafetyNetAttestationStatementSupport;

$this->app->extend(AttestationStatementSupport::class, function ($manager) {
    $manager->add(new AndroidSafetyNetAttestationStatementSupport());
});

Security

These are some details about this WebAuthn implementation:

  • Registration (attestation) is exclusive to the domain, IP and user.
  • Login (assertion) is exclusive to the domain, IP, and the user if specified
  • Cached challenge is always forgotten after resolution, independently of the result.
  • Cached challenge TTL is the same as the WebAuthn timeout (60 seconds default).
  • Included controllers include throttling for WebAuthn endpoints.
  • Users ID (handle) is a random UUID v4.
  • Credentials can be blacklisted (enabled/disabled).

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

As a sidenote, remember to configure your application properly if it's behind a load balancer.

FAQ

  • Does this work with any browser?

Yes. In the case of old browsers, you should have a fallback detection script. This can be asked with the included Javascript helper in a breeze:

if (! Larapass.supportsWebAuthn()) {
   alert('Your device is not secure enough to use this site!');
}
  • Does this stores the user's fingerprint, PIN or patterns in my site?

No. It stores the public key generated by the device.

  • Can a phishing site steal WebAuthn credentials and use them in my site?

No. WebAuthn kills phishing.

  • Can the WebAuthn data identify a particular device?

No, unless explicitly requested and consented.

  • Are my user's classic passwords safe?

Yes, as long you are hashing them as you should, and you have secured your application key. This is done by Laravel by default. You can also disable them.

  • Can a user register two or more devices?

Yes.

  • What happens if a credential is cloned?

The user won't be authenticated since the "logins" counter will be greater than the reported by the original device. To intercede in the procedure, modify the Assertion Validator in the Service Container and add your own CounterChecker:

$this->app->bind(CounterChecker::class, function () {
    return new \App\WebAuthn\MyCountChecker;
});

Inside your counter checker, you may want to throw an exception if the counter is below what is reported.

<?php

namespace App\WebAuthn;

use Webauthn\Counter\CounterChecker;
use App\Exceptions\WebAuthn\CredentialCloned;
use Webauthn\PublicKeyCredentialSource as Credentials;

class MyCountChecker implements CounterChecker
{
    public function check(Credentials $credentials, int $currentCounter) : void
    {
        if ($credentials->getCounter() <= $currentCounter) {
            throw new CredentialCloned($credentials);
        } 
    }
}
  • If a user loses his device, can he register a new device?

Yes, use these recovery helpers.

  • What's the difference between disabling and deleting a credential?

Disabling a credential doesn't delete it, so it can be later enabled manually in the case the user recovers it. When the credential is deleted, it goes away forever.

  • Can a user delete its credentials from its device?

Yes. If it does, the other part of the credentials in your server gets virtually orphaned. You may want to show the user a list of registered credentials to delete them.

  • How secure is this against passwords or 2FA?

Extremely secure since it works only on HTTPS (or localhost), and no password are exchanged, or codes are visible in the screen.

  • Can I deactivate the password fallback? Can I enforce only WebAuthn authentication?

Yes. Just be sure to use the recovery helpers to avoid locking out your users..

  • Does this includes a frontend Javascript?

Yes, but it's very basic.

  • Does this encodes/decode the strings automatically in the frontend?

Yes, the included WebAuthn Helper does it automatically for you.

  • Does this include a credential recovery routes?

Yes.

  • Can I use my smartphone as authenticator through a PC desktop/laptop/terminal?

Depends on the OS and hardware. Some will require previously pairing the device to an "account". Others will only work with USB keys. This is up to hardware and software vendor themselves.

  • Why my device doesn't show Windows Hello/TouchId/FaceId/fingerprint authentication?

By default, this WebAuthn implementation accepts almost everything. Some combinations of devices, OS and web browsers may differ on what to make available for WebAuthn authentication. In other words, it's not my fault.

  • I'm trying to test this in my development server but it doesn't work

Use localhost exclusively, or use ngrok (or similar) to tunnel your site through HTTPS. WebAuthn only works on localhost or HTTPS only.

License

The MIT License (MIT). Please see License File for more information.

Laravel is a Trademark of Taylor Otwell. Copyright © 2011-2020 Laravel LLC.

larapass's People

Contributors

chefe avatar darkghosthunter avatar exodusanto avatar ildyria avatar joe-pritchard avatar lapenna avatar szepeviktor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

larapass's Issues

Problems with MongoDB

I'm testing the package using MongoDB and jenssegers/mongodb as my default database.

The problem is that all models needs to extend from mongo class.

I'm able to make it work by updating src/Eloquent/WebAuthnCredential.php:
from use Illuminate\Database\Eloquent\Model; to use Jenssegers\Mongodb\Eloquent\Model;

so, my question: do you have any suggestion how/where i can override this class?
like copy to App\Models or something like that ...

Malformed UTF-8 characters, possibly incorrectly encoded

On following environment:

PHP 7.4.8
Laravel v7.21
Default configuration for larapass.php

On registration process, I got the error:

Response for /webauthn/register/options:

{"rp":{"name":"Laravel"},"pubKeyCredParams":[{"type":"public-key","alg":-7},{"type":"public-key","alg":-8},{"type":"public-key","alg":-35},{"type":"public-key","alg":-36},{"type":"public-key","alg":-257}],"challenge":"nB15sPSpNaZNc_qo3RexMg","attestation":"none","user":{"name":"[email protected]","id":"MzQ5MDE3ZTEtYjU2Zi00YWM0LTlkYjEtZmNmZGMyOGE2ZTg3","displayName":"admin"},"authenticatorSelection":{"requireResidentKey":false,"userVerification":"preferred"},"timeout":60000}

On /webauthn/register:

Expand { "message": "Malformed UTF-8 characters, possibly incorrectly encoded", "exception": "InvalidArgumentException", "file": "/var/www/vendor/laravel/framework/src/Illuminate/Http/JsonResponse.php", "line": 75, "trace": [ { "file": "/var/www/vendor/symfony/http-foundation/JsonResponse.php", "line": 50, "function": "setData", "class": "Illuminate\\Http\\JsonResponse", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Http/JsonResponse.php", "line": 31, "function": "__construct", "class": "Symfony\\Component\\HttpFoundation\\JsonResponse", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php", "line": 474, "function": "__construct", "class": "Illuminate\\Http\\JsonResponse", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php", "line": 209, "function": "prepareJsonResponse", "class": "Illuminate\\Foundation\\Exceptions\\Handler", "type": "->" }, { "file": "/var/www/app/Exceptions/Handler.php", "line": 53, "function": "render", "class": "Illuminate\\Foundation\\Exceptions\\Handler", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php", "line": 51, "function": "render", "class": "App\\Exceptions\\Handler", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 130, "function": "handleException", "class": "Illuminate\\Routing\\Pipeline", "type": "->" }, { "file": "/var/www/app/Http/Middleware/SetLocale.php", "line": 24, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "App\\Http\\Middleware\\SetLocale", "type": "->" }, { "file": "/var/www/app/Http/Middleware/AuthGates.php", "line": 32, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "App\\Http\\Middleware\\AuthGates", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php", "line": 41, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Illuminate\\Routing\\Middleware\\SubstituteBindings", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php", "line": 44, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Illuminate\\Auth\\Middleware\\Authenticate", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php", "line": 76, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php", "line": 49, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Illuminate\\View\\Middleware\\ShareErrorsFromSession", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php", "line": 116, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php", "line": 62, "function": "handleStatefulRequest", "class": "Illuminate\\Session\\Middleware\\StartSession", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Illuminate\\Session\\Middleware\\StartSession", "type": "->" }, { "file": "/var/www/vendor/stancl/tenancy/src/Middleware/PreventAccessFromCentralDomains.php", "line": 29, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Stancl\\Tenancy\\Middleware\\PreventAccessFromCentralDomains", "type": "->" }, { "file": "/var/www/vendor/stancl/tenancy/src/Middleware/IdentificationMiddleware.php", "line": 36, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/stancl/tenancy/src/Middleware/InitializeTenancyByDomain.php", "line": 38, "function": "initializeTenancy", "class": "Stancl\\Tenancy\\Middleware\\IdentificationMiddleware", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Stancl\\Tenancy\\Middleware\\InitializeTenancyByDomain", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php", "line": 37, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php", "line": 66, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Illuminate\\Cookie\\Middleware\\EncryptCookies", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 103, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Routing/Router.php", "line": 687, "function": "then", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Routing/Router.php", "line": 662, "function": "runRouteWithinStack", "class": "Illuminate\\Routing\\Router", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Routing/Router.php", "line": 628, "function": "runRoute", "class": "Illuminate\\Routing\\Router", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Routing/Router.php", "line": 617, "function": "dispatchToRoute", "class": "Illuminate\\Routing\\Router", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php", "line": 165, "function": "dispatch", "class": "Illuminate\\Routing\\Router", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 128, "function": "Illuminate\\Foundation\\Http\\{closure}", "class": "Illuminate\\Foundation\\Http\\Kernel", "type": "->" }, { "file": "/var/www/vendor/barryvdh/laravel-debugbar/src/Middleware/InjectDebugbar.php", "line": 65, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Barryvdh\\Debugbar\\Middleware\\InjectDebugbar", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php", "line": 21, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php", "line": 21, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php", "line": 27, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize", "type": "->" }, { "file": "/var/www/vendor/fruitcake/laravel-cors/src/HandleCors.php", "line": 37, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Fruitcake\\Cors\\HandleCors", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php", "line": 63, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode", "type": "->" }, { "file": "/var/www/vendor/fideloper/proxy/src/TrustProxies.php", "line": 57, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 167, "function": "handle", "class": "Fideloper\\Proxy\\TrustProxies", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php", "line": 103, "function": "Illuminate\\Pipeline\\{closure}", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php", "line": 140, "function": "then", "class": "Illuminate\\Pipeline\\Pipeline", "type": "->" }, { "file": "/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php", "line": 109, "function": "sendRequestThroughRouter", "class": "Illuminate\\Foundation\\Http\\Kernel", "type": "->" }, { "file": "/var/www/public/index.php", "line": 55, "function": "handle", "class": "Illuminate\\Foundation\\Http\\Kernel", "type": "->" } ] }

I think that the reason may be the encoded id/rawId values, because the exception is thrown while attaching the credentials to the user.

As I saw, in

public function addCredential(CredentialSource $source) : void
{
$this->webAuthnCredentials()->save(
WebAuthnCredential::fromCredentialSource($source)
);
}
and deeper in the code,
$credentials = $this->loader->loadArray($data)->getResponse();
while decoding $json['id'] and $json['rawId'] , we got a non UTF-8 string.

This provokes the following mysql error:

SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xFD\x81\x06[@z...' for column 'id' at row 1 (SQL: insert into `web_authn_credentials` (`id`, `user_handle`, `type`, `transports`, `attestation_type`, `trust_path`, `aaguid`, `public_key`, `counter`, `user_id`, `updated_at`, `created_at`) values (\t²ü\x06[@z!6\x18Bâ¥c\x192P´─╩6ÆÒÝÅ ╩WsE;É, 624905a3-f0ba-4ad1-8186-cc7b5da7cb37, public-key, [], none, {"type":"Webauthn\\TrustPath\\EmptyTrustPath"}, 00000000-0000-0000-0000-000000000000, Ñ\x01\x02\x03& \x01!X \x14­bÿâ^a? TÂפ¨Æ\x18Y·┌©j¾\fr\x16ó░┌í├Ad"X èÄð|áı%Tàóês\x08Æø·F┌+\x04TG¶CK╬┬U┐\x19F=, 1, 1, 2020-07-29 07:01:07, 2020-07-29 07:01:07)) ◀SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xFD\x81\x06[@z...' for column 'id' at row 1 (SQL: insert into `web_authn_credentials` (`id`, `use...

Registration Request sometimes returns empty response

I have a fresh laravel install with laravel/ui --auth initialized.

Then I pulled in your package as described and stuck at Point 5. Your javascript file won't be read from browser unless the # will be changed to '_' or something like that.

After that I changed the register form where the user can register (/register) and added your script to be called on submit. The only route that was called was /webauthn/register/options and it solved in a 401 Unauthorized. I think I did something wrong, but I did not see any. My case is to register a brand new unknown user to the app.

The bug from #14 did I fixed too.

Please help me.

Finger print scanner does not appear. How to fix?

I don't have much experience of webauthn.
My issue is I don't see fingerprint option in the window. (Check attached image) just only usb security key is appearing.
Issue Image : https://drive.google.com/file/d/1KYVs1tKeUe4MJ5_-Eo24RQc-5OXxf5Kr/view?usp=sharing
I get same results using bellow link. It just show only usb security key option. Please check.
https://webauthn.bin.coffee/

But I can fingerprint is working correctly bellow links.
https://webauthn.me/
https://webauthn.io/
My problem is why I don't see fingerprint and pin option when using larapass.

There is another issue the library is not working for browsers in mobile phones. Even Chrome.
I am waiting for solution.
Regards,
Pasindu.

Webauthn working on Chrome(Desktop) but not on android

The package is working fine with Google Chrome Desktop on a Windows 10 PC using windows hello, however in android the devices register successfully as I can see the data on the log but when trying to login the userHandle says NULL while on the desktop the userHandle has the uuid.

Working Google Chrome with Windows 10:

array (
 'id' => 'dJjow4rF261_sEpbZKq1jPCsf122ssRZY9y4aL-qUnE',
 'rawId' => 'dJjow4rF261/sEpbZKq1jPCsf122ssRZY9y4aL+qUnE=',
 'response' =>
 array (
   'authenticatorData' => 'dVNCo7N4HwFvr8OfsnhZHulzNq/CpP4Dcq2AFBnmp4EFAAAAAg==',
   'clientDataJSON' => 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiLVE0ZWdMSTY5bHJMX0otN0xqY09IUSIsIm9yaWdpbiI6Imh0dHBzOi8vc3RhZ2luZy50dXJpY29pbi5uZXQiLCJjcm9zc09yaWdpbiI6ZmFsc2UsIm90aGVyX2tleXNfY2FuX2JlX2FkZGVkX2hlcmUiOiJkbyBub3QgY29tcGFyZSBjbGllbnREYXRhSlNPTiBhZ2FpbnN0IGEgdGVtcGxhdGUuIFNlZSBodHRwczovL2dvby5nbC95YWJQZXgifQ==',
   'signature' => 'odVDuyRMw9YIlx+mzhs76VcKSMvCc7C+TBKmdzZGzWLsuUdqWroAN14vxkuge2w+KKYQnfoXLUAazLe94MhIBBktAPaEvvgSPUq0DB98FMoCzI81BsB2Yi1m32KbPO+OtpEZeaWMeCSddtNTFMntb8vmorvYO1vTwQGBlJidQZS7b39Kh7ixW/G6S3YwwMHVtG/g12/JnpkpzjbzWGvrx7oHgt0vhUevnppuTZhilFvygUkRZeCspEr1KIhP+1w+xyGd4Auom+iXieuHV+hQCyfQc03e3ZO3uauqIwyrFue9CWNJ9tqAxkyupqgcf1BUJfBSLe9Dcr7cdNfjgrw0Ag==',
   'userHandle' => 'ZTc5MWJkZGEtYzRhMS00MjRiLTg2NjItZTU3YzRhYmIzNDVi',
 ),
 'type' => 'public-key',
)

Not Working Google Chrome on Android 10:

array (
  'id' => 'Aaf2Op4CSSB0SenC-JnJ9nGKfCLrq-OO_w2K_I2nYM6vOBWCvWr-r2ECE-1a6y0j93lQIk5rugYwoX37DHZoYig',
  'type' => 'public-key',
  'rawId' => 'Aaf2Op4CSSB0SenC+JnJ9nGKfCLrq+OO/w2K/I2nYM6vOBWCvWr+r2ECE+1a6y0j93lQIk5rugYwoX37DHZoYig=',
  'response' =>
  array (
    'clientDataJSON' => 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiY2dWcGxTa1lfd2N2RHdiYjBrQXRsQSIsIm9yaWdpbiI6Imh0dHBzOlwvXC9zdGFnaW5nLnR1cmljb2luLm5ldCIsImFuZHJvaWRQYWNrYWdlTmFtZSI6ImNvbS5hbmRyb2lkLmNocm9tZSJ9',
    'authenticatorData' => 'dVNCo7N4HwFvr8OfsnhZHulzNq/CpP4Dcq2AFBnmp4EFAAAAAg==',
    'signature' => 'MEUCICcWlIP3H2yeSXHKGTr3SRPS6ES+tRMpLIKv+y28XRzuAiEA3pqJEbBqnZNBjVl+4PNHk3vghKE7DEm7sDiBt5AbjAQ=',
    'userHandle' => NULL,
  ),
)

The userHandle index is empty so this leads to the library response 422

BUG: Invalid byte sequence for encoding "UTF-8"

When attempting to register my YubiKey using Larapass I run into the following error: "SQLSTATE[22021]: Character not in repertoire: 7 ERROR: invalid byte sequence for encoding "UTF8": 0xa5 (SQL: insert into "web_authn_credentials" ("id", "user_handle", "type", "transports", "attestation_type", "trust_path", "aaguid", "public_key", "counter", "user_id", "updated_at", "created_at") values (...)

This appears to be happening on the store of the model into the database (ONLY WHEN USING POSTGRES, not in MySQL) and I can't seem to patch it myself. I have verified that the database is using UTF-8 as its encoding. Any help would be greatly appreciated

Here are the versions I am using:
Laravel 8.x
Postgres 11.5
PHP 8.0

Argument 1 passed to Webauthn\PublicKeyCredentialRpEntity::__construct() must be of the type string, null given

I just installed this package on a Laravel 7 project, when trying to migrate it gives the error, also the project is throwing Error 500.

The full log is:

PHP Fatal error:  Uncaught TypeError: Argument 1 passed to Webauthn\PublicKeyCredentialRpEntity::__construct() must be of the type string, null given, called in /home/douglas/repos/newproject/vendor/darkghosthunter/larapass/src/LarapassServiceProvider.php on line 145 and defined in /home/douglas/repos/newproject/vendor/web-auth/webauthn-lib/src/PublicKeyCredentialRpEntity.php:25
Stack trace:
#0 /home/douglas/repos/newproject/vendor/darkghosthunter/larapass/src/LarapassServiceProvider.php(145): Webauthn\PublicKeyCredentialRpEntity->__construct()
#1 /home/douglas/repos/newproject/vendor/laravel/framework/src/Illuminate/Container/Container.php(805): DarkGhostHunter\Larapass\LarapassServiceProvider::DarkGhostHunter\Larapass\{closure}()
#2 /home/douglas/repos/newproject/vendor/laravel/framework/src/Illuminate/Container/Container.php(691): Illuminate\Container\Container->build()
#3 /home/douglas/repos/newproject/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(796): Illuminate\Container\Container->resol in /home/douglas/repos/newproject/vendor/web-auth/webauthn-lib/src/PublicKeyCredentialRpEntity.php on line 25

I've changed the web-authn version from 3.3.1 to 3.2 manually, made cache:clear route:clear and config:clear, deleted vendor folder and composer.lock and reinstalled everything and the same issue is happening.

Anyone is having the same issue?

Login fails with eiter typeless or email

The registration of a device (android displaylock fingeprint) works perfectly and generates following database entry.

INSERT INTO `web_authn_credentials` (`id`, `user_id`, `name`, `type`, `transports`, `attestation_type`, `trust_path`, `aaguid`, `public_key`, `counter`, `user_handle`, `created_at`, `updated_at`, `disabled_at`) VALUES

('AUlo', 1, NULL, 'public-key', '[]', 'none', '{\"type\":\"Webauthn\\\\TrustPath\\\\EmptyTrustPath\"}', '00000000-0000-0000-0000-000000000000', 0xae, 0, '1afgcc4a-92b7-465f-9967-09c28fgfb2c8', '2021-04-30 13:30:29', '2021-04-30 13:30:29', NULL);

(Data obfuscated :) )

But login with either email or typeless fails with following log entries:

[2021-04-30 15:59:04] local.INFO: Trying to load data from an array {"data":{"id":"lo","rawId":"AAlo=","response":{"authenticatorData":"AAg==","clientDataJSON":"In0=","signature":"Czg==","userHandle":""},"type":"public-key"}} 
[2021-04-30 15:59:04] local.INFO: The data has been loaded  
[2021-04-30 15:59:04] local.DEBUG: Public Key Credential {"publicKeyCredential":{"Webauthn\\PublicKeyCredential":"{}"}} 
[2021-04-30 15:59:04] local.INFO: Checking the authenticator assertion response {"credentialId":"\Z","authenticatorAssertionResponse":{"Webauthn\\AuthenticatorAssertionResponse":[]},"publicKeyCredentialRequestOptions":{"Webauthn\\PublicKeyCredentialRequestOptions":{"challenge":"E","userVerification":"preferred","allowCredentials":[{"type":"public-key","id":"lo"}],"timeout":60000}},"host":"somedomain.de","userHandle":""} 
[2021-04-30 15:59:04] local.ERROR: An error occurred {"exception":"[object] (Assert\\InvalidArgumentException(code: 33): Invalid user handle at vendor/beberlei/assert/lib/Assert/Assertion.php:2723)

(Data obfuscated :) )

Somehow no user handle is available to authenticate against. So maybe either the stored credential data is incorrect or something else is broken.

I followed the implementation guide exactly. (Laravel 8.38)
Any help ? Thanks

Possible values for WEBAUTHN_CACHE?

Hi,

When using the Larapass config file, the challenge configuration includes 'cache' => env('WEBAUTHN_CACHE').
What are possible values for WEBAUTHN_CACHE? Same as CACHE_DRIVER? i.e file

Thx

LARAVEL 8 Support

i got this error when i want to install package in laravel 8

[InvalidArgumentException]
Package darkghosthunter/larapass has a PHP requirement incompatible with your PHP version, PHP extensions and Compo ser version

please fix this issue

BLOB/TEXT column 'id' used in key specification without a key length

While trying to run migration 2020_07_24_075358_create_web_authn_tables I get following error

SQLSTATE[42000]: Syntax error or access violation: 1170 BLOB/TEXT column 'id' used in key specification without a key length (SQL: alter table `web_authn_credentials` add primary key `web_authn_credentials_id_user_id_primary`(`id`, `user_id`))

I'm running this in homestead with

$ mysql --version
mysql  Ver 15.1 Distrib 10.4.13-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2

Any idea what would be sufficient key length?

BUG: Invalid JSON text inserting credentials

It seems that the JSON type doesn't like none as a value for attestation_type

SQLSTATE[22032]: <>: 3140 Invalid JSON text: "Invalid value." at position 1 in value for column 'web_authn_credentials.attestation_type'. (SQL: insert into web_authn_credentials (id, user_handle, type, transports, attestation_type, trust_path, aaguid, public_key, counter, user_id, updated_at, created_at) values (q5Nd7GrQztOkUeWUXA-X_2XiJUc, 076f0803-2531-436d-ae4f-0e4f45a9f6e7, public-key, [], none, {"type":"Webauthn\TrustPath\EmptyTrustPath"}, 00000000-0000-0000-0000-000000000000, ����& �!X �d����"�������@�{0���p����`�x~��"X j��6���2�������6v��'"���:^b.:A�Y, 0, 1, 2021-06-12 13:11:26, 2021-06-12 13:11:26))

To make it work and test TouchID register and login I updated

   'attestation_type' => json_encode($source->getAttestationType()),
   public static function fromCredentialSource(CredentialSource $source)
    {
        return ($model = new static())->fill(
            [
                $model->getKeyName() => $source->getPublicKeyCredentialId(),
                'user_handle' => $source->getUserHandle(),
                'type' => $source->getType(),
                'transports' => $source->getTransports(),
                'attestation_type' => json_encode($source->getAttestationType()),
                'trust_path' => $source->getTrustPath()->jsonSerialize(),
                'aaguid' => $source->getAaguid()->toString(),
                'public_key' => $source->getCredentialPublicKey(),
                'counter' => $source->getCounter(),
            ]
        );
    }

Not sure if that's the proper fix, but it allowed me to test the process.

Yubikey: User handle is mandatory

I have an issue using Yubikeys.

I've created an example application with a fresh Laravel installation and breeze for authentication. My goal is to use this package for a second factor after login.

However, I've just followed your installation guide, use the default published controller and everything.

It works flawlessly with chromes "this device" feature (im on macOS). I can register new keys and login using them.
However, if I try to use Yubikeys, only the registration works. They are stored correctly in the database. When trying to login, I get a 422 response.

I've installed telescope and it shows that the package bails because of an "Assert\InvalidArgumentException: User handle is mandatory" error. The Database entry has the user_handle column filled.

Stacktrace:

/var/www/html/vendor/beberlei/assert/lib/Assert/Assertion.php:642
/var/www/html/vendor/web-auth/webauthn-lib/src/AuthenticatorAssertionResponseValidator.php:125
/var/www/html/vendor/darkghosthunter/larapass/src/WebAuthn/WebAuthnAssertValidator.php:224
/var/www/html/vendor/darkghosthunter/larapass/src/Auth/EloquentWebAuthnProvider.php:85
/var/www/html/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php:422
/var/www/html/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php:371
/var/www/html/vendor/darkghosthunter/larapass/src/Http/AuthenticatesWebAuthn.php:120
/var/www/html/vendor/darkghosthunter/larapass/src/Http/AuthenticatesWebAuthn.php:90
---

Is there something I've misconfigured?

Thanks in advance!

Confirm middleware and login issue

File: WebAuthnAssertValidator

public function validate(array $data)
    {
        if (!$assertion = $this->retrieveAssertion()) {
            return false;
        }
        try {
            $credentials = $this->loader->loadArray($data);
            $response = $credentials->getResponse();
            if (!$response instanceof AuthenticatorAssertionResponse) {
                return false;
            }
            return $this->validator->check(
                $credentials->getRawId(),
                $response,
                $this->retrieveAssertion(),
                $this->request,
                $response->getUserHandle(),
                [$this->getCurrentRpId($assertion)]
            );
        } catch (InvalidArgumentException $exception) {
            return false;
        } finally {
            $this->cache->forget($this->cacheKey());
        }
    }

i think this is some bug $response->getUserHandle(), allways null, no change to the setting will take effect.

but i change $response->getUserHandle(), -> request()->user()->userEntity()->getId(), all working fine..

I think I'm doing something wrong i use yubikeys and i'm tested default settings and change allmoust everything but no luck.

so i ask what needs to be done differently to get the result to $response->getUserHandle(),??

JS helper: Mobile Safari invalid character '#' in function name

Hello and thanks for this amazing package,

During testing I identified that your Javascript helper is not interpreted correctly by the JS engine on Mobile safari (IOS 14)

image

I was able to fix the IOS issue by removing any # in function names.

Hope that help!

best regards

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.