-
Notifications
You must be signed in to change notification settings - Fork 34
Closed
Labels
bugVerified bug by the Nova teamVerified bug by the Nova teamfix incomingA fix is in reviewA fix is in review
Milestone
Description
- Laravel Version: 11.43.2
- Nova Version: 5.4.2
- PHP Version: 8.3.19
- Database Driver & Version: MySql 8.0
- Operating System and Version: Unrelated
- Browser type and version: Unrelated
- Reproduction Repository: Not needed
Description:
When having a custom login pipeline by defining them via Fortify::authenticateThrough and having Nova installed but not set to Nova being the default authentication provider and using Octane, Nova does not respect the customized authentication order and falls back to the default one for fortify.
This happens due to two things:
- When Nova is not the default authentication provider in the
flush/syncmethods of thePendingFortifyConfigurationbasically set the$authenticateThroughcallbacks of Fortify. - Due to using Octane the declared service providers, do not boot up with every request and refresh the custom callbacks anymore, so the information gets lost.
Detailed steps to reproduce the issue on a fresh Nova installation:
- Have your application running on Octane.
- Use Fortify within your main application.
- Customize your authentication pipeline by calling
Fortify::authenticateThroughand passing the custom steps. - Do NOT use nova as your default authentication provider.
- Custom authentication steps will be skipped.
For reference the related NovaServiceProvider and FortifyServiceProvider
NovaServiceProvider:
class NovaServiceProvider extends NovaApplicationServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
parent::boot();
// here we declare all the menu items for nova
Nova::withoutNotificationCenter();
}
/**
* Register the configurations for Laravel Fortify.
*/
protected function fortify(): void
{
// we set the features to null so that nova does not override our 2FA
Nova::fortify()
->features(features: null)
->register();
}
/**
* Register the Nova routes.
*/
protected function routes(): void
{
Nova::routes()
->withoutAuthenticationRoutes()
->withoutPasswordResetRoutes()
->withoutEmailVerificationRoutes()
->register();
}
/**
* Get the dashboards that should be listed in the Nova sidebar.
*
* @return array<int, \Laravel\Nova\Dashboard>
*/
protected function dashboards(): array
{
return [
new \App\Nova\Dashboards\Main,
];
}
/**
* Get the tools that should be listed in the Nova sidebar.
*
* @return array<int, \Laravel\Nova\Tool>
*/
public function tools(): array
{
return [
\Vyuldashev\NovaPermission\NovaPermissionTool::make()
->roleResource(Role::class),
new \Tighten\NovaStripe\NovaStripe,
];
}
/**
* Register any application services.
*/
public function register(): void
{
parent::register();
}
/**
* We are overrideing this in order NOT to be able to go into the admin panel locally and always checking the gate, to be more secure in a potential security risk.
*/
protected function authorization(): void
{
Nova::auth(static function (Request $request): bool {
return Gate::check('viewNova', [Nova::user($request)]);
});
}
}
FortifyServiceProvider
class FortifyServiceProvider extends ServiceProvider
{
public static $loginLimitPerMinute = 5;
/**
* Register any application services.
*/
public function register(): void
{
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Fortify::authenticateThrough(function (Request $request): array {
return [
EnsureLoginIsNotThrottled::class,
CanonicalizeUsername::class,
//our custom middleware that should be passed during login
AttemptToAuthenticate::class,
PrepareAuthenticatedSession::class,
];
});
Fortify::loginView(function () {
return Inertia::render('Auth/Login', [
'canResetPassword' => Route::has('password.request'),
'status' => session('status'),
]);
});
Fortify::confirmPasswordView(function () {
return Inertia::render('Auth/ConfirmPassword');
});
Fortify::twoFactorChallengeView(function (Request $request) {
return inertia('Auth/2FA')->toResponse($request);
});
RateLimiter::for('login', function (Request $request) {
$throttleKey = Str::transliterate(Str::lower($request->input(Fortify::email())).'|'.$request->ip());
return Limit::perMinute(FortifyServiceProvider::$loginLimitPerMinute)->by($throttleKey)->response(function () {
return back()->withErrors([
'email' => 'Too many login attempts. Please try again later.',
]);
});
});
}
}
One possible solution that I tried was creating a sub class of PendingFortifyConfiguration that would override the sync and flush methods. This fixes the issues. But I am not sure if this is the correct way.
I have a couple of questions regarding this:
- Is it intentional behavior that the cache and pipelines should be cleared, when Nova is not the default behavior?
- Is there a setting that we can set to not let this happen?
- Is it okay to override
PendingFortifyConfigurationor would it have any side effects?
Metadata
Metadata
Assignees
Labels
bugVerified bug by the Nova teamVerified bug by the Nova teamfix incomingA fix is in reviewA fix is in review