Installation Run the package installer:
composer require moox/passkey
php artisan mooxpasskey:install
This publishes migrations, config, and sets up the necessary tables (passkey_credentials, passkey_users).
Configure Authentication
Update your config/auth.php to use the passkey guard (if not already configured):
'guards' => [
'web' => [
'driver' => 'passkey',
'provider' => 'users',
],
],
First Endpoint
Use the built-in PasskeyController to handle registration and authentication:
use Moox\Passkey\Http\Controllers\PasskeyController;
Route::get('/register-passkey', [PasskeyController::class, 'showRegistrationForm']);
Route::post('/register-passkey', [PasskeyController::class, 'register']);
Route::post('/login-passkey', [PasskeyController::class, 'authenticate']);
Test with a User
Register a passkey for a test user via the /register-passkey endpoint, then authenticate using the generated credential.
Passkey Registration
PasskeyController::showRegistrationForm().PasskeyController::register(), which:
passkey_credentials.Passkey Authentication
PasskeyController::authenticate() to verify a credential.Fallback Authentication
PasskeyGuard to support traditional password-based fallback:
use Moox\Passkey\Guard;
class CustomGuard extends Guard {
public function authenticateWithPassword($credentials) {
// Custom logic for password fallback
}
}
Laravel Fortify/Passport
Replace or extend Fortify’s login logic to use PasskeyController for passkey-based auth. Example:
// In a custom Fortify provider
public function createLoginResponse(Request $request) {
if ($request->has('passkey')) {
return PasskeyController::authenticate($request);
}
return parent::createLoginResponse($request);
}
Multi-Factor Authentication (MFA) Combine with Laravel’s built-in MFA:
use Moox\Passkey\Facades\Passkey;
public function enablePasskeyMFA(User $user) {
$credential = Passkey::register($user);
$user->mfa_secret = $credential->secret; // Store for MFA
$user->save();
}
Customizing Credential Storage
Override the PasskeyCredential model to add custom fields (e.g., device_name):
namespace App\Models;
use Moox\Passkey\Models\PasskeyCredential as BaseCredential;
class PasskeyCredential extends BaseCredential {
protected $casts = [
'device_name' => 'string',
];
}
Webhook Events
Listen for passkey events (e.g., passkey.registered):
use Moox\Passkey\Events\PasskeyRegistered;
PasskeyRegistered::listen(function (PasskeyRegistered $event) {
// Send email/notification
});
Challenge Expiry
config/passkey.php).Browser/Device Compatibility
if (!Passkey::isSupported()) {
return view('auth.fallback');
}
Credential ID Collisions
id (e.g., from different devices), the second registration may fail.id or validate uniqueness in the PasskeyCredential model:
public static function boot() {
static::creating(function ($model) {
$model->id = Str::uuid()->toString();
});
}
Migration Conflicts
passkey_credentials table schema matches the package’s expectations (e.g., credential_id as primary key).php artisan vendor:publish --tag="passkey-migrations" and compare with the package’s default migration.CORS Issues
'paths' => ['api/passkey/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['https://yourdomain.com'],
'allowed_headers' => ['*'],
Enable Logging
Add to config/passkey.php:
'debug' => env('PASSKEY_DEBUG', false),
Logs will appear in storage/logs/laravel.log.
Inspect Credentials Dump stored credentials for debugging:
use Moox\Passkey\Models\PasskeyCredential;
PasskeyCredential::where('user_id', auth()->id())->dd();
Test with Mock Data
Use Laravel’s Passport or Sanctum mock guards to test passkey flows without real credentials:
Passkey::shouldReceive('verifyCredential')->andReturn(true);
Custom Credential Verification
Override the verifyCredential method in a custom PasskeyService:
namespace App\Services;
use Moox\Passkey\Services\PasskeyService as BaseService;
class CustomPasskeyService extends BaseService {
public function verifyCredential($credentialId, $publicKey, $signature, $challenge) {
// Custom logic (e.g., rate limiting, additional checks)
return parent::verifyCredential($credentialId, $publicKey, $signature, $challenge);
}
}
Bind it in AppServiceProvider:
$this->app->bind(
\Moox\Passkey\Contracts\PasskeyService::class,
App\Services\CustomPasskeyService::class
);
Passkey Metadata
Extend the PasskeyCredential model to store metadata (e.g., device_type, last_used_at):
use Illuminate\Database\Eloquent\Casts\Attribute;
protected function lastUsed(): Attribute {
return Attribute::make(
get: fn () => $this->updated_at->diffForHumans(),
);
}
Rate Limiting
Add rate limiting to the PasskeyController:
use Illuminate\Cache\RateLimiting\Limit;
Route::middleware(['throttle:60,1'])->group(function () {
Route::post('/login-passkey', [PasskeyController::class, 'authenticate']);
});
Passkey Backup Codes Implement backup codes for passkey recovery:
use Moox\Passkey\Facades\Passkey;
$backupCode = Passkey::generateBackupCode();
$user->backup_code = $backupCode;
$user->save();
How can I help you explore Laravel packages today?