diff --git a/README.md b/README.md index cfd6637..89e1c22 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,36 @@ use Vormkracht10\TwoFactorAuth\Pages\TwoFactor; ]) ``` +### Forcing Two Factor Authentication + +If you want to force users to enable Two Factor Authentication, you can add this to your `PanelProvider`: + +```php +->plugins([ + TwoFactorAuthPlugin::make()->forced(), +]) +``` + +> [!WARNING] +> When you're using the `forced` method, make sure to set the `multi_tenancy` option to `true` in the `filament-two-factor-auth.php` config file when you're using a multi-tenant setup. Otherwise, the forced setting will not work. We cannot check the tenant in the `PanelProvider` because the user is not authenticated yet. + +#### Customizing the forced message + +If you want to customize the forced message, you can publish the language file: + +```bash +php artisan vendor:publish --tag="filament-two-factor-auth-translations" +``` + +Then you can customize the message in the `lang/vendor/filament-two-factor-auth/en.json` file. You should change the following keys: + +```json +{ + "Your administrator requires you to enable two-factor authentication.": "Your custom message here.", + "Two-Factor Authentication mandatory": "Your custom title here." +} +``` + ## Testing ```bash diff --git a/resources/lang/nl.json b/resources/lang/nl.json index d4a4f8e..074ad6c 100644 --- a/resources/lang/nl.json +++ b/resources/lang/nl.json @@ -8,6 +8,10 @@ "Confirm": "Bevestigen", "Deactivate": "Deactiveer", "Email": "E-mail", + "Generate new recovery codes": "Genereer nieuwe herstelcodes", + "Hello": "Hallo", + "If you didn't try to log in, please change your password immediately to protect your account.": "Als u niet heeft geprobeerd in te loggen, wijzig dan onmiddellijk uw wachtwoord om uw account te beschermen.", + "Kind regards": "Met vriendelijke groet", "Login": "Inloggen", "Or scan the QR code with your authenticator app": "Of scan de QR-code met uw authenticator-app", "Password Confirmation": "Wachtwoordbevestiging", @@ -19,19 +23,17 @@ "Secure your account": "Beveilig uw account", "Submit": "Verzenden", "Successfully resend the OTP code": "OTP-code succesvol opnieuw verzonden", + "The provided two factor authentication code was invalid.": "De verstrekte tweestapsverificatiecode was ongeldig.", "The secret key to setup the authenticator app is": "De geheime sleutel om de authenticator-app in te stellen is", "Two-Factor Authentication": "Tweestapsverificatie", "Two-Factor Authentication enabled": "Tweestapsverificatie ingeschakeld", + "Two-Factor Authentication mandatory": "Tweestapsverificatie verplicht", "Verify": "Verifiëren", + "You can disable two factor authentication at any time by using the button below": "U kunt tweestapsverificatie op elk moment uitschakelen met de onderstaande knop", "You have :amount options to confirm your identity, please choose one of the options below to continue": "U heeft :amount opties om uw identiteit te bevestigen, kies een van de onderstaande opties om door te gaan", + "You recently requested to log in to your account. To complete the login, please use the following two-factor authentication (2FA) code:": "U heeft onlangs gevraagd om in te loggen op uw account. Gebruik de volgende tweestapsverificatie (2FA) code om de login te voltooien:", "Your account has been secured with two factor authentication": "Uw account is beveiligd met tweestapsverificatie", + "Your administrator requires you to enable two-factor authentication.": "Uw beheerder vereist dat u tweestapsverificatie inschakelt.", "Your security code for :app": "Uw beveiligingscode voor :app", - "Your security code is": "Uw beveiligingscode is", - "The provided two factor authentication code was invalid.": "De verstrekte tweestapsverificatiecode was ongeldig.", - "Generate new recovery codes": "Genereer nieuwe herstelcodes", - "You can disable two factor authentication at any time by using the button below": "U kunt tweestapsverificatie op elk moment uitschakelen met de onderstaande knop", - "You recently requested to log in to your account. To complete the login, please use the following two-factor authentication (2FA) code:": "U heeft onlangs gevraagd om in te loggen op uw account. Gebruik de volgende tweestapsverificatie (2FA) code om de login te voltooien:", - "If you didn't try to log in, please change your password immediately to protect your account.": "Als u niet heeft geprobeerd in te loggen, wijzig dan onmiddellijk uw wachtwoord om uw account te beschermen.", - "Kind regards": "Met vriendelijke groet", - "Hello": "Hallo" + "Your security code is": "Uw beveiligingscode is" } \ No newline at end of file diff --git a/resources/views/two-factor.blade.php b/resources/views/two-factor.blade.php index 1aebeab..f93836f 100644 --- a/resources/views/two-factor.blade.php +++ b/resources/views/two-factor.blade.php @@ -1,9 +1,10 @@ +

- {{ __('Secure your account') }} + {{ __('Secure your account') }} sdfsadfsa

@if (!$showingRecoveryCodes && $user->two_factor_confirmed_at) diff --git a/src/Http/Middleware/ForceTwoFactor.php b/src/Http/Middleware/ForceTwoFactor.php new file mode 100644 index 0000000..7658dbb --- /dev/null +++ b/src/Http/Middleware/ForceTwoFactor.php @@ -0,0 +1,31 @@ +user(); + + if ($request->is('*/two-factor') || $request->is('*/logout')) { + return $next($request); + } + + if ($user && ! $user->two_factor_confirmed_at) { + $currentPanel = Filament::getCurrentPanel(); + + if ($currentPanel) { + return redirect()->to(route('filament.' . $currentPanel->getId() . '.pages.two-factor', [ + 'tenant' => Filament::getTenant(), + ]))->with('two_factor_redirect_message', __('Your administrator requires you to enable two-factor authentication.')); + } + } + + return $next($request); + } +} \ No newline at end of file diff --git a/src/Pages/TwoFactor.php b/src/Pages/TwoFactor.php index a543908..4e4fd73 100644 --- a/src/Pages/TwoFactor.php +++ b/src/Pages/TwoFactor.php @@ -60,6 +60,15 @@ public function getTitle(): string | Htmlable public function mount(): void { + if (session('two_factor_redirect_message')) { + Notification::make() + ->title(__('Two-Factor Authentication mandatory')) + ->body(session('two_factor_redirect_message')) + ->danger() + ->persistent() + ->send(); + } + $this->twoFactorOptionsCount = config('filament-two-factor-auth.options') ? count(config('filament-two-factor-auth.options')) : 0; $this->user = Auth::user(); @@ -88,7 +97,7 @@ public function getConfirmationForm(): array return [ TextInput::make('current_password') ->label(__('Password')) - ->dehydrateStateUsing(fn ($state) => filled($state)) + ->dehydrateStateUsing(fn($state) => filled($state)) ->required() ->password() ->inlineLabel() @@ -272,4 +281,4 @@ public function regenerateRecoveryCodes(GenerateNewRecoveryCodes $generate): voi $this->showingRecoveryCodes = true; } -} +} \ No newline at end of file diff --git a/src/TwoFactorAuthPlugin.php b/src/TwoFactorAuthPlugin.php index 7e57d9f..d355e19 100644 --- a/src/TwoFactorAuthPlugin.php +++ b/src/TwoFactorAuthPlugin.php @@ -3,12 +3,16 @@ namespace Vormkracht10\TwoFactorAuth; use Filament\Contracts\Plugin; +use Filament\Facades\Filament; use Filament\Navigation\MenuItem; use Filament\Panel; +use Vormkracht10\TwoFactorAuth\Http\Middleware\ForceTwoFactor; use Vormkracht10\TwoFactorAuth\Pages\TwoFactor; class TwoFactorAuthPlugin implements Plugin { + private bool $forced = false; + public function getId(): string { return 'filament-two-factor-auth'; @@ -24,12 +28,19 @@ public function register(Panel $panel): void ]) ->viteTheme('vendor/vormkracht10/filament-2fa/resources/dist/filament-two-factor-auth.css'); + if ($this->isForced()) { + $middlewareMethod = config('filament-two-factor-auth.enabled_features.multi_tenancy') ? 'tenantMiddleware' : 'middleware'; + $panel->$middlewareMethod([ + ForceTwoFactor::class, + ]); + } + if (! config('filament-two-factor-auth.enabled_features.multi_tenancy')) { $panel->userMenuItems([ 'two-factor-authentication' => MenuItem::make() ->icon('heroicon-o-lock-closed') ->label(__('Two-Factor Authentication')) - ->url(fn (): string => TwoFactor::getUrl()), + ->url(fn(): string => TwoFactor::getUrl()), ]); } @@ -55,4 +66,16 @@ public static function get(): static return $plugin; } -} + + public function forced(bool $forced = true, bool $withTenancy = false): self + { + $this->forced = $forced; + + return $this; + } + + public function isForced(): bool + { + return $this->forced; + } +} \ No newline at end of file