# 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.