# Filament Two Factor Authentication (2FA)
Filament Two Factor Authentication is a Laravel package that adds Google 2FA (TOTP) and Passkey authentication to Filament admin panels. It provides a complete two-factor authentication solution including QR code setup, recovery codes, login challenges, and optional enforcement of 2FA for all users. The package integrates seamlessly with Filament's panel system and supports multiple authentication methods.
The package leverages `pragmarx/google2fa` for TOTP generation and verification, `bacon/bacon-qr-code` for QR code rendering, and `spatie/laravel-passkeys` for WebAuthn passkey support. It includes Livewire components for user-facing 2FA management, middleware for challenge enforcement, and a comprehensive event system for tracking authentication lifecycle events.
## Installation
Install the package via Composer and run the installation command.
```bash
# Install the package
composer require stephenjude/filament-two-factor-authentication
# Run the installation command to publish migrations
php artisan filament-two-factor-authentication:install
# Run migrations to add 2FA columns to users table
php artisan migrate
# Optionally publish views for customization
php artisan vendor:publish --tag="filament-two-factor-authentication-views"
```
## User Model Setup
Add the `TwoFactorAuthenticatable` trait and `HasPasskeys` interface to your User model.
```php
'datetime',
'two_factor_confirmed_at' => 'datetime',
];
public function canAccessPanel(\Filament\Panel $panel): bool
{
return true;
}
}
```
## TwoFactorAuthenticationPlugin::make()
Creates and configures the Two Factor Authentication plugin instance for a Filament panel. This is the main entry point for enabling 2FA features in your application.
```php
default()
->id('admin')
->path('admin')
->login()
->plugins([
TwoFactorAuthenticationPlugin::make()
->enableTwoFactorAuthentication() // Enable Google 2FA (TOTP)
->enablePasskeyAuthentication() // Enable Passkey/WebAuthn
->addTwoFactorMenuItem() // Add 2FA settings to user menu
->forceTwoFactorSetup(), // Force all users to setup 2FA
]);
}
}
```
## enableTwoFactorAuthentication()
Enables Google Authenticator (TOTP) based two-factor authentication with configurable challenge middleware.
```php
enableTwoFactorAuthentication(
condition: true, // Enable/disable conditionally
challengeMiddleware: TwoFactorChallenge::class // Custom middleware for 2FA challenge
);
// Conditional enabling based on environment
TwoFactorAuthenticationPlugin::make()
->enableTwoFactorAuthentication(
condition: fn () => app()->environment('production'),
);
```
## enablePasskeyAuthentication()
Enables WebAuthn passkey authentication allowing users to log in using biometrics or hardware security keys.
```php
enablePasskeyAuthentication(condition: true);
// Enable passkey only for specific conditions
TwoFactorAuthenticationPlugin::make()
->enablePasskeyAuthentication(
condition: fn () => config('app.enable_passkeys', true)
);
```
## forceTwoFactorSetup()
Forces all users to set up two-factor authentication before accessing the panel. Users without 2FA enabled will be redirected to the setup page.
```php
enableTwoFactorAuthentication()
->forceTwoFactorSetup(
condition: true, // Enable enforcement
requiresPassword: true, // Require password confirmation
middleware: ForceTwoFactorSetup::class // Custom enforcement middleware
);
// Force 2FA setup only for admin users
TwoFactorAuthenticationPlugin::make()
->enableTwoFactorAuthentication()
->forceTwoFactorSetup(
condition: fn () => auth()->user()?->is_admin ?? false,
requiresPassword: false,
);
```
## addTwoFactorMenuItem()
Adds a menu item to the user menu for accessing 2FA settings.
```php
enableTwoFactorAuthentication()
->addTwoFactorMenuItem(
condition: true, // Show/hide the menu item
label: 'Security Settings', // Custom menu label
icon: 'heroicon-o-shield-check' // Custom Heroicon
);
// Default configuration
TwoFactorAuthenticationPlugin::make()
->enableTwoFactorAuthentication()
->addTwoFactorMenuItem(); // Uses default label "2FA" and lock icon
```
## TwoFactorAuthenticatable Trait
The trait provides methods for managing 2FA state on the User model. It handles secret storage, recovery codes, QR code generation, and authentication state.
```php
hasEnabledTwoFactorAuthentication();
// Returns: true/false
// Check if user has any passkeys registered
$hasPasskeys = $user->hasEnabledPasskeyAuthentication();
// Returns: true/false
// Get recovery codes (decrypted)
$codes = $user->recoveryCodes();
// Returns: ['abc123-def456', 'ghi789-jkl012', ...]
// Replace a used recovery code with a new one
$user->replaceRecoveryCode('abc123-def456');
// Get QR code SVG for authenticator app setup
$svg = $user->twoFactorQrCodeSvg();
// Returns: ''
// Get QR code URL (otpauth:// format)
$url = $user->twoFactorQrCodeUrl();
// Returns: 'otpauth://totp/AppName:user@email.com?secret=...'
// Check if 2FA challenge has been passed in current session
$passed = $user->isTwoFactorChallengePassed();
// Returns: true/false
// Mark 2FA challenge as passed (used after successful verification)
$user->setTwoFactorChallengePassed();
```
## EnableTwoFactorAuthentication Action
Programmatically enables two-factor authentication for a user by generating a secret key and recovery codes.
```php
twoFactorQrCodeSvg();
$secretKey = decrypt($user->two_factor_secret);
```
## ConfirmTwoFactorAuthentication Action
Confirms the 2FA setup by verifying a code from the user's authenticator app. This finalizes the 2FA enrollment.
```php
errors();
// ['data.code' => ['The provided two factor authentication code was invalid.']]
}
```
## DisableTwoFactorAuthentication Action
Disables two-factor authentication for a user by clearing all 2FA-related data.
```php
recoveryCodes();
// Returns array of 8 recovery codes like: ['abc123-def456', ...]
// Dispatches RecoveryCodesGenerated event
```
## TwoFactorAuthenticationProvider
The core provider for generating secrets, QR code URLs, and verifying TOTP codes.
```php
generateSecretKey();
// Returns: 'JBSWY3DPEHPK3PXP' (Base32 encoded)
// Generate QR code URL for authenticator apps
$qrUrl = $provider->qrCodeUrl(
companyName: 'My Application',
companyEmail: 'user@example.com',
secret: $secret
);
// Returns: 'otpauth://totp/My%20Application:user@example.com?secret=JBSWY3DPEHPK3PXP'
// Verify a TOTP code
$isValid = $provider->verify(
secret: $secret,
code: '123456'
);
// Returns: true/false
```
## TwoFactorChallenge Middleware
Middleware that intercepts authenticated requests and redirects users to the 2FA challenge page if they haven't passed verification.
```php
is('api/*')) {
return $next($request);
}
return parent::handle($request, $next);
}
protected function twoFactorChallengeRoute(): ?string
{
// Custom challenge route
return route('custom.2fa.challenge');
}
}
```
## ForceTwoFactorSetup Middleware
Middleware that forces users to set up 2FA before accessing any other page in the panel.
```php
is('api/*')) {
return $next($request);
}
return parent::handle($request, $next);
}
protected function redirectTo(): ?string
{
// Custom setup page URL
return route('custom.2fa.setup');
}
}
```
## Livewire Components
Embed the 2FA management components in custom profile pages or settings views.
```php
{{-- In a Blade view --}}
{{-- Two Factor Authentication Management --}}
@livewire(\Stephenjude\FilamentTwoFactorAuthentication\Livewire\TwoFactorAuthentication::class)
{{-- Passkey Management --}}
@livewire(\Stephenjude\FilamentTwoFactorAuthentication\Livewire\PasskeyAuthentication::class)
{{-- With custom configuration --}}
@livewire(\Stephenjude\FilamentTwoFactorAuthentication\Livewire\TwoFactorAuthentication::class, [
'aside' => false, // Layout mode
'redirectTo' => '/dashboard' // Redirect after setup
])
```
## Event Listeners
Subscribe to 2FA lifecycle events in your EventServiceProvider to implement custom logic like logging, notifications, or audit trails.
```php
[
\App\Listeners\LogTwoFactorEnabled::class,
],
// User confirmed 2FA with valid code (setup complete)
TwoFactorAuthenticationConfirmed::class => [
\App\Listeners\SendTwoFactorConfirmationEmail::class,
],
// User disabled 2FA
TwoFactorAuthenticationDisabled::class => [
\App\Listeners\LogTwoFactorDisabled::class,
\App\Listeners\NotifySecurityTeam::class,
],
// User was presented with 2FA challenge during login
TwoFactorAuthenticationChallenged::class => [
\App\Listeners\LogChallengeAttempt::class,
],
// User provided incorrect 2FA code
TwoFactorAuthenticationFailed::class => [
\App\Listeners\LogFailedAttempt::class,
\App\Listeners\CheckForBruteForce::class,
],
// User successfully passed 2FA challenge
ValidTwoFactorAuthenticationCodeProvided::class => [
\App\Listeners\LogSuccessfulVerification::class,
],
// User successfully used a recovery code
ValidTwoFactorRecoveryCodeProvided::class => [
\App\Listeners\AlertRecoveryCodeUsed::class,
],
// User generated new recovery codes
RecoveryCodesGenerated::class => [
\App\Listeners\LogRecoveryCodesRegenerated::class,
],
// A single recovery code was replaced after use
RecoveryCodeReplaced::class => [
\App\Listeners\LogRecoveryCodeReplacement::class,
],
];
}
```
## Event Listener Implementation Example
Create custom listeners to handle 2FA events with business logic.
```php
user;
$cacheKey = "2fa_failed_attempts:{$user->id}";
// Track failed attempts
$attempts = cache()->increment($cacheKey);
cache()->put($cacheKey, $attempts, now()->addMinutes(15));
Log::warning('2FA verification failed', [
'user_id' => $user->id,
'email' => $user->email,
'attempts' => $attempts,
'ip' => request()->ip(),
]);
// Alert on multiple failed attempts
if ($attempts >= 3) {
Notification::send($user, new SuspiciousLoginAttempt([
'attempts' => $attempts,
'ip_address' => request()->ip(),
'user_agent' => request()->userAgent(),
]));
}
}
}
```
## Database Migration
The package migration adds the required columns to the users table.
```php
text('two_factor_secret')
->after('password')
->nullable();
// Encrypted JSON array of recovery codes
$table->text('two_factor_recovery_codes')
->after('two_factor_secret')
->nullable();
// Timestamp when 2FA was confirmed (setup completed)
$table->timestamp('two_factor_confirmed_at')
->after('two_factor_recovery_codes')
->nullable();
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn([
'two_factor_secret',
'two_factor_recovery_codes',
'two_factor_confirmed_at',
]);
});
}
};
```
## Complete Panel Configuration
Full example showing all available plugin options with advanced configurations.
```php
default()
->id('admin')
->path('admin')
->login()
->colors([
'primary' => '#3b82f6',
])
->plugins([
TwoFactorAuthenticationPlugin::make()
// Enable Google Authenticator 2FA
->enableTwoFactorAuthentication(
condition: true,
challengeMiddleware: TwoFactorChallenge::class,
)
// Enable Passkey/WebAuthn authentication
->enablePasskeyAuthentication(
condition: true,
)
// Force all users to setup 2FA
->forceTwoFactorSetup(
condition: fn () => app()->environment('production'),
requiresPassword: true,
middleware: ForceTwoFactorSetup::class,
)
// Add 2FA settings to user menu
->addTwoFactorMenuItem(
condition: true,
label: 'Two-Factor Auth',
icon: 'heroicon-o-finger-print',
),
]);
}
}
```
## Summary
Filament Two Factor Authentication provides a complete, production-ready two-factor authentication solution for Laravel Filament applications. The primary use cases include securing admin panels with TOTP-based authentication, implementing passwordless login via WebAuthn passkeys, enforcing 2FA policies for compliance requirements, and providing users with self-service 2FA management. The package handles the entire authentication lifecycle including setup, verification, recovery codes, and account security events.
Integration follows a simple pattern: install the package, add the trait to your User model, configure the plugin on your Filament panel, and optionally customize the behavior through middleware, events, and Livewire components. The event system enables building audit trails, security alerts, and custom business logic around authentication events. The package supports both optional 2FA (user choice) and mandatory 2FA (organization policy) through its flexible middleware configuration.