marcelweidum/filament-passkeys
Add passkey (WebAuthn) authentication to your Filament app via Spatie Laravel Passkeys. Includes migrations, routes, and a simple Panel plugin, with profile integration for managing user passkeys. Compatible with Filament v5 (3.x).
Installation:
composer require marcelweidum/filament-passkeys
Publish the config file (if needed):
php artisan vendor:publish --provider="MarcelWeidum\FilamentPasskeys\FilamentPasskeysServiceProvider" --tag="filament-passkeys-config"
Configure Authentication:
Ensure your Filament user model (App\Models\User) uses the HasPasskeys trait from the spatie/laravel-passkeys package:
use MarcelWeidum\FilamentPasskeys\Traits\HasPasskeys;
class User extends Authenticatable
{
use HasPasskeys;
}
First Use Case:
Replace the default Filament login form with the passkey-enabled one by registering the PasskeysLoginForm in your Filament panel provider:
use MarcelWeidum\FilamentPasskeys\Widgets\PasskeysLoginForm;
public function panel(Panel $panel): Panel
{
return $panel
->login()
->form(PasskeysLoginForm::class);
}
Seamless Migration:
PasskeysLoginForm widget to handle both passkey and fallback login methods.Customization:
PasskeysLoginForm class:
use MarcelWeidum\FilamentPasskeys\Widgets\PasskeysLoginForm;
class CustomPasskeysLoginForm extends PasskeysLoginForm
{
protected static ?string $passkeyRegistrationTitle = 'Register Your Biometric Key';
protected static ?string $passkeyLoginTitle = 'Sign in with Your Device';
}
Multi-Factor Authentication (MFA):
Passkey::verify() method in custom auth logic.Registration Flow:
use MarcelWeidum\FilamentPasskeys\Facades\Passkey;
public function register(Request $request)
{
$user = User::create($request->validated());
Passkey::createForUser($user); // Auto-enroll in passkey flow
}
API Integration:
spatie/laravel-passkeys API for custom endpoints:
use MarcelWeidum\FilamentPasskeys\Facades\Passkey;
Route::post('/api/passkey/register', function (Request $request) {
return Passkey::create($request->user(), $request->input('publicKeyCredential'));
});
Browser Compatibility:
userAgent in PasskeysLoginForm.Database Schema:
users table has the passkey_public_key and passkey_credential_id columns (migrated by spatie/laravel-passkeys).2.x to 3.x:
php artisan migrate
Passkey Sync:
// In a Filament resource action
public static function getActions(UserResource $resource): array
{
return [
Actions\ForcePasskeyReset::make(),
];
}
Testing:
spatie/laravel-passkeys's testing helpers or mock the Passkey facade:
Passkey::shouldReceive('verify')->andReturn(true);
Performance:
Logs:
config/filament-passkeys.php:
'debug' => env('APP_DEBUG', false),
storage/logs/laravel.log for passkey-related errors (e.g., WebAuthnException).WebAuthn Errors:
NotAllowedError: User not verified (e.g., session expired).NotSupportedError: Browser/OS doesn’t support WebAuthn.Passkey::errors() method to surface these to users.Configuration Quirks:
APP_URL is correctly set in .env—passkeys rely on HTTPS and accurate origin verification.passkey_allowed_domains in the config:
'passkey_allowed_domains' => [
'app.yourdomain.com',
'staging.yourdomain.com',
],
Custom Verification Logic:
verifyPasskey method in a service class:
use MarcelWeidum\FilamentPasskeys\Contracts\PasskeyVerifier;
class CustomPasskeyVerifier implements PasskeyVerifier
{
public function verify($credentialId, $publicKey, $signature, $user)
{
// Add custom logic (e.g., rate limiting, IP checks)
return Passkey::verify($credentialId, $publicKey, $signature, $user);
}
}
AppServiceProvider:
PasskeyVerifier::macro('custom', function () {
return new CustomPasskeyVerifier();
});
Passkey Metadata:
Passkey model:
class User extends Authenticatable
{
use HasPasskeys;
protected $casts = [
'passkey_metadata' => 'json',
];
}
$metadata = $user->passkeys->first()->metadata;
Passkey Events:
// In EventServiceProvider
protected $listen = [
\MarcelWeidum\FilamentPasskeys\Events\PasskeyRegistered::class => [
\App\Listeners\LogPasskeyRegistration::class,
],
];
Filament Policy Integration:
use MarcelWeidum\FilamentPasskeys\Widgets\PasskeysLoginForm;
PasskeysLoginForm::make()
->canRegister(function (User $user) {
return $user->hasRole('admin');
});
How can I help you explore Laravel packages today?