Skip to content

Laravel Nova does not respect nova-impersonate guard config #6796

@steffanhalv

Description

@steffanhalv
  • Laravel Version: 11.43.0
  • Nova Version: 4.27.0
  • PHP Version: 8.3.17
  • Database Driver & Version: Mysql 8.2
  • Operating System and Version: Unrelated
  • Browser type and version: Unrelated
  • Reproduction Repository: Not needed

Description:

Laravel Nova does not respect nova-impersonate guard config when multiple guards using same provider and driver.

Detailed steps to reproduce the issue on a fresh Nova installation:

config/nova-impersonate.php

<?php

return [

    'default_impersonator_guard' => 'nova',
    'impersonator_guards' => ['nova'],

Notice: In laravel < 10 this would work as Laravel Nova selects the first defined guard, but in laravel 11 the web guard is always added to the top

Related Laravel issue: laravel/framework#55165
config/auth.php

'guards' => [
        'nova' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
    ]

The authGuard will always resolve to 'web' from Laravel 11 and up ...

vendor/laravel/nova/src/Http/Controllers/ImpersonateController.php

public function startImpersonating(NovaRequest $request, ImpersonatesUsers $impersonator)
    {
        if ($impersonator->impersonating($request)) {
            return $this->stopImpersonating($request, $impersonator);
        }

        /** @var class-string<\Illuminate\Contracts\Auth\Authenticatable&\Illuminate\Database\Eloquent\Model> $userModel */
        $userModel = with(Nova::modelInstanceForKey($request->input('resource')), function ($model) {
            return ! is_null($model) ? get_class($model) : Util::userModel();
        });

        $authGuard = Util::sessionAuthGuardForModel($userModel);

        $currentUser = Nova::user($request);

        /** @var \Illuminate\Contracts\Auth\Authenticatable&\Illuminate\Database\Eloquent\Model $user */
        $user = $userModel::findOrFail($request->input('resourceId'));

        // Now that we're guaranteed to be a 'real' user, we'll make sure we're
        // actually trying to impersonate someone besides ourselves, as that
        // would be unnecessary.
        if (! $currentUser->is($user)) {
            abort_unless(optional($currentUser)->canImpersonate() ?? false, 403);
            abort_unless(optional($user)->canBeImpersonated() ?? false, 403);

            $impersonator->impersonate(
                $request,
                Auth::guard($authGuard),
                $user
            );
        }

        return $impersonator->redirectAfterStartingImpersonation($request);
    }

Because this looks for the first guard having the given driver and provider (which always will be 'web' in >= Laravel 11)

vendor/laravel/nova/src/Util.php

public static function sessionAuthGuardForModel($model)
    {
        if (is_object($model)) {
            $model = get_class($model);
        }

        $provider = collect(config('auth.providers'))->reject(function ($provider) use ($model) {
            return ! ($provider['driver'] === 'eloquent' && is_a($model, $provider['model'], true));
        })->keys()->first();

        return collect(config('auth.guards'))->reject(function ($guard) use ($provider) {
            return ! ($guard['driver'] === 'session' && $guard['provider'] === $provider);
        })->keys()->first();
    }

Solution

Always respect the default_impersonator_guard config.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions