Skip to content

Nova overrides custom authentication pipeline when using Octane and not being the default authentication prrovider #6797

@PNardman

Description

@PNardman
  • 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:

  1. When Nova is not the default authentication provider in the flush/ syncmethods of the PendingFortifyConfiguration basically set the $authenticateThrough callbacks of Fortify.
  2. 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:

  1. Have your application running on Octane.
  2. Use Fortify within your main application.
  3. Customize your authentication pipeline by calling Fortify::authenticateThrough and passing the custom steps.
  4. Do NOT use nova as your default authentication provider.
  5. 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:

  1. Is it intentional behavior that the cache and pipelines should be cleared, when Nova is not the default behavior?
  2. Is there a setting that we can set to not let this happen?
  3. Is it okay to override PendingFortifyConfiguration or would it have any side effects?

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugVerified bug by the Nova teamfix incomingA fix is in review

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions