laragear/webauthn
Laravel package to authenticate users with WebAuthn passkeys (biometrics, device keys). Provides request validation and login flow with attestation/assertion support, compatible with Octane. Note: superseded by laravel/passkeys and unmaintained.
Installation:
composer require laragear/webauthn
php artisan webauthn:install
php artisan migrate
Configure Auth:
Update config/auth.php:
'providers' => [
'users' => [
'driver' => 'eloquent-webauthn',
'model' => App\Models\User::class,
'password_fallback' => true,
],
]
Add Trait/Contract to User Model:
use Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable;
use Laragear\WebAuthn\WebAuthnAuthentication;
class User implements WebAuthnAuthenticatable
{
use WebAuthnAuthentication;
}
Register Routes (in web.php):
use Laragear\WebAuthn\Http\Routes;
Route::middleware('web')->group(function () {
Routes::register()->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class);
});
First Use Case:
/webauthn/register/options to get attestation options, then POST to /webauthn/register with the authenticator response./webauthn/login/options to get assertion options, then POST to /webauthn/login with the authenticator response.Generate Options:
public function registerOptions(AttestationRequest $request)
{
return $request->toCreate();
}
fastRegistration() or secureRegistration() to control user verification.userless() for resident keys (one-touch login).allowDuplicates() to bypass existing credential checks.Handle Response:
public function register(AttestedRequest $request)
{
$request->save(); // Saves to `webauthn_credentials` table
return response()->json(['message' => 'Passkey registered!']);
}
$request->save(fn ($credential) => $credential->alias = $request->alias);
Generate Options:
public function loginOptions(AssertionRequest $request)
{
return $request->toAssert();
}
Handle Response:
public function login(AssertedRequest $request)
{
$user = $request->login(); // Automatically validates and logs in
return response()->json(['user' => $user]);
}
Override default controllers in routes/web.php:
Routes::register(
attest: 'custom/register',
assert: 'custom/login'
);
@laragear/webpass for JavaScript:
const { success } = await Webpass.attest('/webauthn/register/options', '/webauthn/register');
password_fallback: true in auth.php for hybrid auth.WebAuthnTestCase for unit/feature tests:
use Laragear\WebAuthn\Testing\WebAuthnTestCase;
class WebAuthnTest extends WebAuthnTestCase { ... }
CSRF/XSRF:
@laragear/webpass, manually add tokens for non-exempt routes.VerifyCsrfToken:
Routes::register()->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class);
User Authentication:
AttestationRequest/AttestedRequest require authenticated users. For guest registration:
Auth::login($user);
$request->toCreate();
Auth::logout();
WebAuthn::attest()/WebAuthn::assert() manually:
$options = WebAuthn::attest($user);
$credential = WebAuthn::verify($response, $options);
Resident Keys:
userless() requires user verification on assertion. Test with biometric/PIN authenticators.Duplicate Credentials:
allowDuplicates() bypasses exclusion checks but may cause authenticator confusion.Key Storage:
webauthn_credentials. Ensure the table has:
Schema::create('webauthn_credentials', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->json('public_key_credential');
$table->json('transports')->nullable();
$table->json('metadata')->nullable();
$table->timestamps();
});
AttestedRequest/AssertedRequest for validation exceptions. Log raw responses:
\Log::debug('WebAuthn Response:', $request->all());
ext-openssl is enabled.ext-sodium or paragonie/sodium_compat.Custom Credential Model:
Extend Laragear\WebAuthn\Models\WebAuthnCredential:
class CustomCredential extends WebAuthnCredential { ... }
Update config/webauthn.php:
'model' => App\Models\CustomCredential::class,
Custom Attestation/Assertion Logic:
Override WebAuthnAuthentication trait methods:
public function getWebAuthnCredentials()
{
return $this->credentials()->where('active', true);
}
Event Listeners: Listen for credential events:
WebAuthnCredentialCreated::dispatch($credential);
config/webauthn.php has correct rp_id (e.g., yourdomain.com).config/webauthn.php:
'challenge_timeout' => 300, // 5 minutes
getExcludedCredentialIds():
public function getExcludedCredentialIds()
{
return $this->credentials()->pluck('id')->toArray();
}
How can I help you explore Laravel packages today?