Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
core45 opened this issue Mar 8, 2022 · 62 comments
Open

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

core45 opened this issue Mar 8, 2022 · 62 comments

Comments

@core45
Copy link

core45 commented Mar 8, 2022

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');

@core45
Copy link
Author

core45 commented Mar 8, 2022

Ah. And I'm using Jetsream

@relaypilot
Copy link

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

@relaypilot
Copy link

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,
    ];

@core45
Copy link
Author

core45 commented Mar 10, 2022

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.

@core45
Copy link
Author

core45 commented Mar 10, 2022

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.

@relaypilot
Copy link

relaypilot commented Mar 15, 2022

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.

@luigel
Copy link

luigel commented Mar 16, 2022

@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.

@seabasss
Copy link

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?

@Coding-Kiwi
Copy link

@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

@seabasss
Copy link

seabasss commented Apr 4, 2022

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.

@Mahegus
Copy link

Mahegus commented Apr 27, 2022

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.

@mkamranmalik72
Copy link

Facing the same issue any luck?

@seabasss
Copy link

seabasss commented May 3, 2022

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

@Mahegus
Copy link

Mahegus commented May 3, 2022

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());
});

@invaders-xx
Copy link

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 ?

@m7vm7v
Copy link

m7vm7v commented Jun 8, 2022

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?

@Mahegus
Copy link

Mahegus commented Jun 8, 2022

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.

@illusive-ch
Copy link

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());
    });
    ```

@Mahegus
Copy link

Mahegus commented Jun 10, 2022

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.

@illusive-ch
Copy link

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

@Mahegus
Copy link

Mahegus commented Jun 10, 2022

@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.

@illusive-ch
Copy link

@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

@Mahegus
Copy link

Mahegus commented Jun 13, 2022

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

@dominik-eller
Copy link

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

@heychazza
Copy link

Still facing this issue with Jetstream..

@apydevs
Copy link

apydevs commented Dec 8, 2022

@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?

@dominik-eller
Copy link

@

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?

@apydevs
Copy link

apydevs commented Dec 8, 2022

@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',

@dominik-eller
Copy link

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; ?

@apydevs
Copy link

apydevs commented Dec 8, 2022

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

@dominik-eller
Copy link

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

@apydevs
Copy link

apydevs commented Dec 8, 2022

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

@dominik-eller
Copy link

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

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

@apydevs
Copy link

apydevs commented Dec 8, 2022

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?

@dominik-eller
Copy link

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.

@apydevs
Copy link

apydevs commented Dec 8, 2022

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.

@dominik-eller
Copy link

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.

@apydevs
Copy link

apydevs commented Dec 8, 2022

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

@seabasss
Copy link

seabasss commented Dec 8, 2022

I'm using this:

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

@dominik-eller
Copy link

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?

@apydevs
Copy link

apydevs commented Dec 8, 2022

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

@apydevs
Copy link

apydevs commented Dec 8, 2022

I'm using this:

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

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

@dominik-eller
Copy link

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

@seabasss
Copy link

seabasss commented Dec 8, 2022

Only sometimes if I visit their profile page.

@apydevs
Copy link

apydevs commented Dec 8, 2022

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 ?

@seabasss
Copy link

seabasss commented Dec 8, 2022

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.

@mudasirhamid
Copy link

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

@Piket564
Copy link

Piket564 commented Nov 3, 2023

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?

@JasperTey
Copy link

What I had to do to get things working on a Laravel 10.42.0 Jetstream/Inertia app:

In the boot method of EventServiceProvider:

Event::listen(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());
});

And also ensuring that the Impersonation routes explicitly had auth:web middleware.

Route::controller(ImpersonateUserController::class)
    ->middleware(['auth:web']) // this is the important part
    ->prefix('/impersonate')
    ->as('impersonate.')
    ->group(function () {
        Route::post('/{user}', 'store')
            ->name('store');

        Route::delete('/', 'destroy')
            ->name('destroy');
    });

@zawilliams
Copy link

For anyone dealing with this still and not using the \Lab404\Impersonate\Events\TakeImpersonation class, I just did this without changing the auth:sanctum stuff and added this to the boot method of my EventServiceProvider:

Event::listen(function (\Laravel\Nova\Events\StartedImpersonating $event) {
    session()->put('password_hash_sanctum', $event->impersonated->getAuthPassword());
});

Event::listen(\Laravel\Nova\Events\StoppedImpersonating::class, function(\Laravel\Nova\Events\StoppedImpersonating $event) {
    session()->put('password_hash_sanctum', $event->impersonator->getAuthPassword());
});

Works.

@zawilliams
Copy link

Spoke maybe too soon. Once I'm impersonating, I can't get back to Nova. I just need to log out of the impersonated user and then log back in as myself. Not sure if this is intended behavior?

@seabasss
Copy link

Spoke maybe too soon. Once I'm impersonating, I can't get back to Nova. I just need to log out of the impersonated user and then log back in as myself. Not sure if this is intended behavior?

Doesn't Nova come with impersonation built-in now?
I set up a route to stop impersonation like this.

    Route::get('/stopimpersonation', function (Request $request, ImpersonatesUsers $impersonator) {
        if ($impersonator->impersonating($request)) {
            $impersonator->stopImpersonating($request, Auth::guard('web'), User::class);

            return redirect('/nova/resources/users/');
        }

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

@zawilliams
Copy link

It does but it keeps logging me out. Also using Jetstream. Honestly didn't realize I was posting in a different package's issues. I think I need to go to bed 🫤

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests