bbs-lab / nova-force-two-factor
Force every Laravel Nova admin to enrol in two-factor authentication.
Requires
- php: ^8.2
- bbs-lab/nova-toast: ^1.0
- illuminate/contracts: ^11.0 || ^12.0 || ^13.0
- illuminate/support: ^11.0 || ^12.0 || ^13.0
- laravel/nova: ^5.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- bbs-lab/nova-password-rotation: ^2.0
- larastan/larastan: ^3.0
- laravel/pint: ^1.18
- nunomaduro/collision: ^8.0
- orchestra/testbench: ^9.0 || ^10.0 || ^11.0
- orchestra/workbench: ^9.0 || ^10.0 || ^11.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- pestphp/pest-plugin-mutate: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Make Laravel Nova's built-in two-factor authentication mandatory. Nova's 2FA (via Fortify) is opt-in; this package adds a middleware that redirects any authenticated admin who has not enrolled to the User Security page — with a toast explaining why — until they set it up.
The redirect notice is shown with bbs-lab/nova-toast.
Requirements
- PHP
^8.2 - Laravel Nova
^5.0 - Laravel
^11.0 || ^12.0 || ^13.0
Nova's built-in two-factor authentication must be enabled (Nova::fortify() with
Features::twoFactorAuthentication()), and your Nova user must carry Fortify's
Laravel\Fortify\TwoFactorAuthenticatable trait — the middleware reads its
hasEnabledTwoFactorAuthentication() method. A user model without that method is left alone.
Nova's built-in User Security / 2FA page (
nova.pages.user-security) is Nova 5 only, so this package requires Nova 5. It is exercised in CI on Laravel 11/12/13 and PHP 8.3/8.4/8.5.
Installation
Because Nova is a paid, private package, make sure your application is already authenticated against
nova.laravel.com, then:
composer require bbs-lab/nova-force-two-factor
The service provider is auto-discovered and injects the middleware into Nova's stack for you — there is nothing else to wire up. It is enforced by default.
Configuration
Publish the config if you want to tweak it:
php artisan vendor:publish --tag="nova-force-two-factor-config"
return [ // Enforce 2FA enrolment. Set NOVA_FORCE_TWO_FACTOR=false to disable (e.g. locally). 'enabled' => (bool) env('NOVA_FORCE_TWO_FACTOR', true), // Let the package inject the middleware into Nova's stack. Turn off to wire it up yourself. 'auto_register_middleware' => (bool) env('NOVA_FORCE_TWO_FACTOR_AUTO_MIDDLEWARE', true), // Requests matching any of these are let through even when the admin has not enrolled. 'except' => [ 'routes' => [ 'nova-password-rotation.expired.*', ], 'paths' => [ // ], ], ];
except.routesis matched with$request->routeIs(...)andexcept.pathswith$request->is(...). Both accept the wildcards Laravel understands, so exact route names, route-name patterns, exact URLs and URL patterns are all covered.- Nova's assets, logout and the User Security enrolment subtree are always allowed and cannot be blocked — they don't need listing (and can't be removed), so an admin can never lock themselves out or break the SPA.
The toast wording lives in the package translations; publish them to customise:
php artisan vendor:publish --tag="nova-force-two-factor-translations"
How it works
The middleware runs through Nova's nova middleware group, which is nested inside the api/asset stacks
too — so it sees page visits and every script/style/XHR request. It treats each kind correctly:
- Full-page navigation (a GET that accepts HTML) →
302redirect to User Security, with a toast. - Inertia visit (
X-Inertia) →409+X-Inertia-Location, with a toast (Nova redirects client-side). - Background XHR → never redirected: reads pass through, writes are blocked with
403. Redirecting these would break the SPA and enrolment itself.
An admin who has enrolled, an anonymous request, and the allow-listed routes/paths all pass straight through.
Manual middleware registration
Set auto_register_middleware to false and add the middleware where you want it, e.g. in config/nova.php:
use BBSLab\NovaForceTwoFactor\Http\Middleware\EnsureTwoFactorEnabled; 'middleware' => [ // … EnsureTwoFactorEnabled::class, ],
Testing
composer test # Pest suite composer test-coverage # 100% line coverage on src/ composer test-mutation # mutation testing (MSI ≥ 80%) composer analyse # PHPStan level 8 composer format # Pint (laravel preset + strict types)
A full embedded Nova app (via Orchestra Workbench) lets you exercise the flow in a real Nova instance:
composer serve # boots Nova at http://localhost:8000/nova
Security
The middleware only gates access and flashes a message — it stores no data and exposes no endpoint. If you
discover a security issue, please email paris@big-boss-studio.com instead of using the issue tracker.
Changelog
Please see CHANGELOG for what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Credits
License
The MIT License (MIT). Please see License File for more information.