404labfr / laravel-impersonate

Laravel Impersonate is a plugin that allows you to authenticate as your users.

Home Page:https://marceau.casals.fr

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

After Auth::user()->impersonate($otherUser) I'm getting logged out

core45 opened this issue · comments

I've installed the package as described and tried to impersonate other user.
But I'm always getting logged out.

Here is my code:

$anotherUser = User::find(9);
Auth::user()->impersonate($anotherUser);
return redirect()->route('dashboard');

Ah. And I'm using Jetsream

I'm experiencing the same issue after updating composer:

  • Upgrading laravel/framework (v9.3.1 => v9.4.1)

It was working fine with Laravel 9.3.1, and breaks after upgrading to 9.4.x

Here is the Laravel changelog: https://github.com/laravel/framework/blob/9.x/CHANGELOG.md#v940---2022-03-08

Quick fix is to comment out the following line in Illuminate\Foundation\Http\Kernel.php:

    protected $middlewarePriority = [
        \Illuminate\Cookie\Middleware\EncryptCookies::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
        \Illuminate\Routing\Middleware\ThrottleRequests::class,
        \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
        // Comment out this line:
        // \Illuminate\Contracts\Session\Middleware\AuthenticatesSessions::class,
        // Add this line:
        \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ];

I do not have anything like \Illuminate\Contracts\Session\Middleware\AuthenticatesSessions::class, in Illuminate\Foundation\Http\Kernel.php

I tried to comment out this line instead:
\Laravel\Jetstream\Http\Middleware\AuthenticateSession::class,

but it did not help. I'm still getting log out.

And I'd like to comment one important thing to the maintainer of this package.
I can understand that the package can not work with some other software like JetStream.
But for a f..k sake why didn't you warn us about it?
I really appreciate your hard work and I know this package is free and comes with no guarantee whatsoever but I feel that respect should be mutual.
I just lost many hours trying to make work something which is faulty. If the author was honest and put any information about the situation in the readme it would spare me hours of useless work.

This should now be fixed if you upgrade to Laravel Framework 9.5.1: laravel/framework#41491

Update: after checking, it didn't resolve it for me, still experiencing the same issue.

@core45 I fixed this one by updating the middleware of the routes generated by jetstream

-Route::group(['middleware' => ['auth:sanctum', 'verified']], function (): void {
+Route::group(['middleware' => ['auth:web', 'verified']], function (): void {
//
});

Im using laravel nova to impersonate and nova is using a web auth guard. So the nova and jetstream was not using the same guard. Because by default the jetstream's guard is using sanctum.

This package when impersonating is logging in the user using the current guard which is web but redirects to the dashboard in jetstream which is a sanctum guard.

So, either removing \Laravel\Jetstream\Http\Middleware\AuthenticateSession::class from App\Http\Kernel.php or changing sanctum to web in the routes file seems to fix this with Jetstream. Does anyone know the implications of doing any of those options?

commented

@seabasss
The main reason why auth:sanctum is in your routes/web.php can be read in the docs

You may be wondering why we suggest that you authenticate the routes within your application's routes/web.php file using the sanctum guard. Remember, Sanctum will first attempt to authenticate incoming requests using Laravel's typical session authentication cookie. If that cookie is not present then Sanctum will attempt to authenticate the request using a token in the request's Authorization header. In addition, authenticating all requests using Sanctum ensures that we may always call the tokenCan method on the currently authenticated user instance:

So basically if you do not need sanctum in your web routes, use auth:web
Which is what I did and it fixed the issue.

But nevertheless it would be interesting to know why impersonation with auth:sanctum worked for me in laravel 8 and not in laravel 9

Thanks for the info! It actually worked in 9 for me up until v9.4. And it works with sanctum for me after updating some files to reflec latest jetstream, however it doesn’t work if I add the new jetstream config parameter in the route middleware.

commented

A simple solution to get the impersonating working for a Jetstream/Sanctum setup is to use the event TakeImpersonation and add the missing session key required for Sanctum authentification

Event::listen(\Lab404\Impersonate\Events\TakeImpersonation::class,
    function(\Lab404\Impersonate\Events\TakeImpersonation $event) {
    session()->put('password_hash_sanctum', $event->impersonated->getAuthPassword());
});

still working on the LeaveImpersonation.

Facing the same issue any luck?

A simple solution to get the impersonating working for a Jetstream/Sanctum setup is to use the event TakeImpersonation and add the missing session key required for Sanctum authentification

Event::listen(\Lab404\Impersonate\Events\TakeImpersonation::class,
    function(\Lab404\Impersonate\Events\TakeImpersonation $event) {
    session()->put('password_hash_sanctum', $event->impersonated->getAuthPassword());
});

still working on the LeaveImpersonation.

Where do you put that? Thx

commented

You put it in the boot method of your EventServiceProvider

Event::listen(function(\Lab404\Impersonate\Events\TakeImpersonation $event) {
    session()->put('password_hash_sanctum', $event->impersonated->getAuthPassword());
});

I am using latest L9, without Jetstream, and impersonating gets me to login page. If I comment AuthenticateSession middleware, it works. Did anyone face this issue too ? How did you solve it ?

A simple solution to get the impersonating working for a Jetstream/Sanctum setup is to use the event TakeImpersonation and add the missing session key required for Sanctum authentification

Event::listen(\Lab404\Impersonate\Events\TakeImpersonation::class,
    function(\Lab404\Impersonate\Events\TakeImpersonation $event) {
    session()->put('password_hash_sanctum', $event->impersonated->getAuthPassword());
});

still working on the LeaveImpersonation.

All cool and beans mate, thanks a lot, I think this is the cleanest of the options.

Were you able to get the LeaveImpersonation working too?

commented

A simple solution to get the impersonating working for a Jetstream/Sanctum setup is to use the event TakeImpersonation and add the missing session key required for Sanctum authentification

Event::listen(\Lab404\Impersonate\Events\TakeImpersonation::class,
    function(\Lab404\Impersonate\Events\TakeImpersonation $event) {
    session()->put('password_hash_sanctum', $event->impersonated->getAuthPassword());
});

still working on the LeaveImpersonation.

All cool and beans mate, thanks a lot, I think this is the cleanest of the options.

Were you able to get the LeaveImpersonation working too?

Sadly not, Its not currently a problem, as when logging in as an admin user, i just redirect to the admin area instead.

I was able to get leave working thanks to @matt127127 @m7vm7v looks like there is an impersonator object on the event, for anyone else just throw this in a listener or right on the web.php

Event::listen(\Lab404\Impersonate\Events\TakeImpersonation::class,
    function(\Lab404\Impersonate\Events\TakeImpersonation $event) {
        session()->put('password_hash_sanctum', $event->impersonated->getAuthPassword());
    });
Event::listen(\Lab404\Impersonate\Events\LeaveImpersonation::class,
    function(\Lab404\Impersonate\Events\LeaveImpersonation $event) {
        session()->put('password_hash_sanctum', $event->impersonator->getAuthPassword());
    });
    ```
commented

I was able to get leave working thanks to @matt127127 @m7vm7v looks like there is an impersonator object on the event, for anyone else just throw this in a listener or right on the web.php

Event::listen(\Lab404\Impersonate\Events\TakeImpersonation::class,
    function(\Lab404\Impersonate\Events\TakeImpersonation $event) {
        session()->put('password_hash_sanctum', $event->impersonated->getAuthPassword());
    });
Event::listen(\Lab404\Impersonate\Events\LeaveImpersonation::class,
    function(\Lab404\Impersonate\Events\LeaveImpersonation $event) {
        session()->put('password_hash_sanctum', $event->impersonator->getAuthPassword());
    });
    ```

@illusive-ch I have tried using the LeaveImpersonation::class but to no avail, If you manage to get it working i would love to see how you did it.

@matt127127 thats exactly what I put in my routes file to get it to stop from logging out when u leave impersonation.

commented

@matt127127 thats exactly what I put in my routes file to get it to stop from logging out when u leave impersonation.

@illusive-ch What version of Laravel are you using, i'm using 9.17 and it just doesn't want to play ball for me.

@matt127127

> art --version
Laravel Framework 9.13.0

When I set this up I setup my own routes as well let me get the code for you:
web.php

Event::listen(\Lab404\Impersonate\Events\TakeImpersonation::class,
    function(\Lab404\Impersonate\Events\TakeImpersonation $event) {
        session()->put('password_hash_sanctum', $event->impersonated->getAuthPassword());
    });
Event::listen(\Lab404\Impersonate\Events\LeaveImpersonation::class,
    function(\Lab404\Impersonate\Events\LeaveImpersonation $event) {
        session()->put('password_hash_sanctum', $event->impersonator->getAuthPassword());
    });
Route::middleware([
    'superadmin'])
    ->name('admin.')
     ->prefix('admin')
     ->group(function () {
         Route::resource('user', \App\Http\Controllers\Admin\UserController::class);
         Route::get('user/{user}/impersonate', '\App\Http\Controllers\Admin\UserController@impersonate')->name('user.impersonate');
         Route::get('user/{user}/leave-impersonate', '\App\Http\Controllers\Admin\UserController@leaveImpersonate')->name('user.leave-impersonate');
     });

Usercontroller.php

    public function impersonate(User $user)
    {
        auth()->user()->impersonate($user);

        return redirect()->route('dashboard');
    }
    public function leaveImpersonate()
    {
        auth()->user()->leaveImpersonation();

        return redirect()->route('dashboard');
    }

Let me know if that works for you

commented

@illusive-ch Sadly that didn't work, it seemed to make it worse for me, i couldn't even impersonate.

For me it is working with default setup of this module without any events on local valet setup but not on production server (forge). I am using spatie/laravel-permissons and sanctum with Jetstream

Still facing this issue with Jetstream..

Event::listen(\Lab404\Impersonate\Events\TakeImpersonation::class,
    function(\Lab404\Impersonate\Events\TakeImpersonation $event) {
        session()->put('password_hash_sanctum', $event->impersonated->getAuthPassword());
    });
Event::listen(\Lab404\Impersonate\Events\LeaveImpersonation::class,
    function(\Lab404\Impersonate\Events\LeaveImpersonation $event) {
        session()->put('password_hash_sanctum', $event->impersonator->getAuthPassword());
    });
Route::impersonate();

Can you please try this?

Event::listen(\Lab404\Impersonate\Events\TakeImpersonation::class,
    function(\Lab404\Impersonate\Events\TakeImpersonation $event) {
        session()->put('password_hash_sanctum', $event->impersonated->getAuthPassword());
    });
Event::listen(\Lab404\Impersonate\Events\LeaveImpersonation::class,
    function(\Lab404\Impersonate\Events\LeaveImpersonation $event) {
        session()->put('password_hash_sanctum', $event->impersonator->getAuthPassword());
    });
Route::impersonate();

Can you please try this?

This does not seem to work. After a fresh deploy I could impersonate once. Leaving this impersonation did not work and further impersonation did not work as well. I am getting logged out.

On my local machine the default is working (without any need of manipulation). On the laravel forge server with deploying over envoyer it is not working - even with your manipulation.

I'm using Laravel 8 + Inertia Js for authentication using Fortify but impersonating not working logged out as well, kindly someone helps me out with this issue, maybe it is working on simple Laravel older versions but in with Inertia js + Fortify it is not working I tried all above solutions.

With jetstream check the jetstream config file, as this also has 'guard' set to sanctum change this to 'web'

` /*
|--------------------------------------------------------------------------
| Jetstream Guard
|--------------------------------------------------------------------------
|
| Here you may specify the authentication guard Jetstream will use while
| authenticating users. This value should correspond with one of your
| guards that is already present in your "auth" configuration file.
|
*/

'guard' => 'web',`

With jetstream check the jetstream config file, as this also has 'guard' set to sanctum change this to 'web'

` /* |-------------------------------------------------------------------------- | Jetstream Guard |-------------------------------------------------------------------------- | | Here you may specify the authentication guard Jetstream will use while | authenticating users. This value should correspond with one of your | guards that is already present in your "auth" configuration file. | */

'guard' => 'web',`

There is no "guard" section in my jetstream config file.

With jetstream check the jetstream config file, as this also has 'guard' set to sanctum change this to 'web'
` /* |-------------------------------------------------------------------------- | Jetstream Guard |-------------------------------------------------------------------------- | | Here you may specify the authentication guard Jetstream will use while | authenticating users. This value should correspond with one of your | guards that is already present in your "auth" configuration file. | */

'guard' => 'web',`

There is no "guard" section in my jetstream config file.

I'm currently on version 2.12.6 of Jetstream, what version are you currently running?

2.9.0 but I checke the latest v.2 release and the master branch on GitHub and there is no guard section as well.

With jetstream check the jetstream config file, as this also has 'guard' set to sanctum change this to 'web'
` /* |-------------------------------------------------------------------------- | Jetstream Guard |-------------------------------------------------------------------------- | | Here you may specify the authentication guard Jetstream will use while | authenticating users. This value should correspond with one of your | guards that is already present in your "auth" configuration file. | */

'guard' => 'web',`

There is no "guard" section in my jetstream config file.

I'm currently on version 2.12.6 of Jetstream, what version are you currently running?

2.9.0 but I checke the latest v.2 release and the master branch on GitHub and there is no guard section as well.

2.9.0 but I checke the latest v.2 release and the master branch on GitHub and there is no guard section as well.

With jetstream check the jetstream config file, as this also has 'guard' set to sanctum change this to 'web'
` /* |-------------------------------------------------------------------------- | Jetstream Guard |-------------------------------------------------------------------------- | | Here you may specify the authentication guard Jetstream will use while | authenticating users. This value should correspond with one of your | guards that is already present in your "auth" configuration file. | */

'guard' => 'web',`

There is no "guard" section in my jetstream config file.

I'm currently on version 2.12.6 of Jetstream, what version are you currently running?

2.9.0 but I checke the latest v.2 release and the master branch on GitHub and there is no guard section as well.

I believe the config is generated from this stub
https://github.com/laravel/jetstream/blob/2.x/stubs/config/jetstream.php Line 47

As im sure you know using php artisan vendor:publish will give you a list and in there will be jetstream-config, when this is selected this way I'm sure it generates the jetstream config from the stub in the link.

If you have customised the config for jetstream try coping the
'guard' => 'sanctum', changing to web in your current config file.

Hope this helps

2.9.0 but I checke the latest v.2 release and the master branch on GitHub and there is no guard section as well.

With jetstream check the jetstream config file, as this also has 'guard' set to sanctum change this to 'web'
` /* |-------------------------------------------------------------------------- | Jetstream Guard |-------------------------------------------------------------------------- | | Here you may specify the authentication guard Jetstream will use while | authenticating users. This value should correspond with one of your | guards that is already present in your "auth" configuration file. | */

'guard' => 'web',`

There is no "guard" section in my jetstream config file.

I'm currently on version 2.12.6 of Jetstream, what version are you currently running?

2.9.0 but I checke the latest v.2 release and the master branch on GitHub and there is no guard section as well.

I believe the config is generated from this stub https://github.com/laravel/jetstream/blob/2.x/stubs/config/jetstream.php Line 47

As im sure you know using php artisan vendor:publish will give you a list and in there will be jetstream-config, when this is selected this way I'm sure it generates the jetstream config from the stub in the link.

If you have customised the config for jetstream try coping the 'guard' => 'sanctum', changing to web in your current config file.

Hope this helps

There is no guard directive. I just updated via composer and did a vendor publish...

Bildschirm­foto 2022-12-08 um 20 52 21

@arumcomputer Only trying to assist ....
Do a search of the repo there are many references to the jetstream config using config('jetstream.guard')
https://github.com/laravel/jetstream/search?p=1&q=guard

What do you have in your LaravelProject/config/jetstream.php file?

@

I am very very thankful for your assist. Waited so long...
I guess it is just named "middleware" instead of "guard". Here is my jetstream config:

'stack' => 'livewire',
'middleware' => ['web'],
'features' => [
// Features::termsAndPrivacyPolicy(),
// Features::profilePhotos(),
// Features::api(),
Features::teams(['invitations' => true]),
//Features::accountDeletion(),
],
'profile_photo_disk' => 'public',

But I do find "jetstream.guard" in the vendor folder. Maybe I can just add that directive to my config file?

@arumcomputer, No problem, let's see if we can get it sorted.
You should be able just to add it in, I have both middleware and guard see my config below

'stack' => 'livewire',
'middleware' => ['web'],
'auth_session' => AuthenticateSession::class,
'guard' => 'web',
'features' => [
    // Features::termsAndPrivacyPolicy(),
    // Features::profilePhotos(),
    // Features::api(),
    Features::teams(['invitations' => true]),
    Features::accountDeletion(),
],
'profile_photo_disk' => 'public',

ok, just added 'guard' => 'web' and it does not work for me. But what is that...
'auth_session' => AuthenticateSession::class
?

Which class do you use here?
Laravel\Jetstream\Http\Middleware\AuthenticateSession; ?

Yeah its
use Laravel\Jetstream\Http\Middleware\AuthenticateSession;

@apydevs does not fix it. I can't believe it's to hard.

In your web route, files are you is auth: sanctum. anywhere. ?

In your web route, files are you is auth: sanctum. anywhere. ?

Yes, I am using auth:sanctum in a route group.

In your web route, files are you is auth: sanctum. anywhere. ?

Yes, I am using auth:sanctum in a route group.

Out of interest does change this to auth:web make a difference?

In your web route, files are you is auth: sanctum. anywhere. ?

Yes, I am using auth:sanctum in a route group.

Out of interest does change this to auth:web make a difference?

with auth:web instead of auth:sanctum the impersonation works as expected. But I don't know if any other authentication does throw an unexpected exception now.

In your web route, files are you is auth: sanctum. anywhere. ?

Yes, I am using auth:sanctum in a route group.

Out of interest does change this to auth:web make a difference?

with auth:web instead of auth:sanctum the impersonation works as expected. But I don't know if any other authentication does throw an unexpected exception now.

Well that's the first step getting it working as expected , i know sanctum is built more for SPA & mobile applications, and simple, token based APIs. From what i can tell your using the livewire stack, so shouldn't affect it. if you planning on using the user API feature within jetstream it may be worth going through and checking that & accessing from postman just to be sure.

I am using auth:sanctum in api route and my app is working. I tested the frontend as well and it seems to work. I ask myself why I was using auth:sanctum in web route.

I think you will find when you did the php artisan jetstream:install livewire --teams

it changes all the auth:web to auth:sanctum

https://github.com/laravel/jetstream/blob/bfed92ddacb22053ddfe1208f098fbd9d9404d89/src/Console/InstallCommand.php. Line 293

I'm using this:

Route::middleware([
    'auth:web,sanctum',
    config('jetstream.auth_session'),
    'verified',
])

I think you will find when you did the php artisan jetstream:install livewire --teams

it changes all the auth:web to auth:sanctum

https://github.com/laravel/jetstream/blob/bfed92ddacb22053ddfe1208f098fbd9d9404d89/src/Console/InstallCommand.php. Line 293

oh yes, I am using team feature. Does it need to be auth:sanctum for that or will it be fine with auth:web?

Do you have another solution to use impersonation with auth:sanctum?

@arumcomputer Not with auth: sanctum as of yet, I'm using teams with Auth: web and it's been fine.

I'm using this:

Route::middleware([
    'auth:web,sanctum',
    config('jetstream.auth_session'),
    'verified',
])

@seabasss does this log you out when impersonating a user?

ok i will continue using auth:web
hope to be notified when auth:sanctum and impersonation will work.

Only sometimes if I visit their profile page.

Only sometimes if I visit their profile page.
@seabasss
Thinking about it, if you publish the jetstream route file and add the config('jetstream.auth_session') to the middleware array, wouldn't this sort the profile issues you experience? As the jetstream route contains all the relevant routes for user profile etc ?

Only sometimes if I visit their profile page.
@seabasss
Thinking about it, if you publish the jetstream route file and add the config('jetstream.auth_session') to the middleware array, wouldn't this sort the profile issues you experience? As the jetstream route contains all the relevant routes for user profile etc ?

Good call. It hasn't been too much of an issue for me and I think it was some weird thing that if I impersonated one user it worked, but if I switched and visited the profile page I got logged out.

You put it in the boot method of your EventServiceProvider

Event::listen(function(\Lab404\Impersonate\Events\TakeImpersonation $event) {
    session()->put('password_hash_sanctum', $event->impersonated->getAuthPassword());
});

Thanks a lot

Hi, I don't think the problem has been resolved.
I keep getting logged out after I try to impersonate any user.
Below are some details:

web.php

Route::middleware(['auth:web', config('jetstream.auth_session'), 'verified',])->group(function () {
    Route::impersonate();
    /* other routes */
});

Livewire Function

public function ImpersonateUser($id = null)
    {
        if ($id != null) {
            session()->put(['impersonate' => $id]);
            Auth::user()->impersonate(User::find($id));
            $this->redirect(route('dashboard'));
        }
    }
app.php
```php
    'providers' => ServiceProvider::defaultProviders()->merge([
        /*
         * Package Service Providers...
         */

        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,
        App\Providers\FortifyServiceProvider::class,
        App\Providers\JetstreamServiceProvider::class,
        Lab404\Impersonate\ImpersonateServiceProvider::class,
    ])->toArray(),

composer.json

{
  "name": "laravel/laravel",
  "type": "project",
  "description": "The skeleton application for the Laravel framework.",
  "keywords": [
    "laravel",
    "framework"
  ],
  "license": "MIT",
  "require": {
    "php": "^8.1",
    "danielme85/laravel-log-to-db": "^4.0",
    "djokicpn/laravel-email-audit-log": "^1.0",
    "guzzlehttp/guzzle": "^7.2",
    "lab404/laravel-impersonate": "^1.7",
    "laravel/framework": "^10.10",
    "laravel/jetstream": "^4.0",
    "laravel/sanctum": "^3.2",
    "laravel/tinker": "^2.8",
    "livewire/livewire": "^3.0",
    "power-components/livewire-powergrid": "^5.1",
    "spatie/laravel-medialibrary": "^10.0.0",
    "spatie/laravel-permission": "^5.11",
    "wire-elements/modal": "^2.0",
    "wireui/wireui": "*"
  },
  "require-dev": {
    "barryvdh/laravel-debugbar": "^3.9",
    "fakerphp/faker": "^1.9.1",
    "laravel/pint": "^1.0",
    "laravel/sail": "^1.18",
    "mockery/mockery": "^1.4.4",
    "nunomaduro/collision": "^7.0",
    "phpunit/phpunit": "^10.1",
    "spatie/laravel-ignition": "^2.0"
  },
  "autoload": {
    "psr-4": {
      "App\\": "app/",
      "Database\\Factories\\": "database/factories/",
      "Database\\Seeders\\": "database/seeders/"
    },
    "exclude-from-classmap": [
      "vendor/livewire/livewire/src/Features/SupportLegacyModels/EloquentModelSynth.php"
    ],
    "files": [
      "app/Http/General.php",
      "app/Overrides/EloquentModelSynth.php"
    ]
  },
  "autoload-dev": {
    "psr-4": {
      "Tests\\": "tests/"
    }
  },
  "scripts": {
    "post-autoload-dump": [
      "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
      "@php artisan package:discover --ansi"
    ],
    "post-update-cmd": [
      "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
    ],
    "post-root-package-install": [
      "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
    ],
    "post-create-project-cmd": [
      "@php artisan key:generate --ansi"
    ]
  },
  "extra": {
    "laravel": {
      "dont-discover": []
    }
  },
  "config": {
    "optimize-autoloader": true,
    "preferred-install": "dist",
    "sort-packages": true,
    "allow-plugins": {
      "pestphp/pest-plugin": true,
      "php-http/discovery": true
    }
  },
  "minimum-stability": "stable",
  "prefer-stable": true
}

Any idea?