Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

whoisthisstud's avatar

Impersonate User issue

Not sure if this is the best approach, but attempting to add Impersonate User functionality to site, so that Admins can provide support for users.

public function switchUser($id)
{
    if ( ! Session::has('existing_user_id') ) {
        Session::put( 'existing_user_id' , Auth::id() );
        Session::put( 'user_is_switched' , true );
        Session::save();
    } 

    Auth::loginUsingId($id);
    return redirect()->route('dashboard-home');
}

Locally, the code works without issue. On the server, it redirects to the dashboard but does not login to the impersonated user, and after a couple of seconds I get a 419 page expired when one of my livewire components polls the server. If I watch the session table, I can see my session record nulling the user_id field.

I'm not opposed to using the 404labs package, but would prefer to limit dependencies as much as possible.

Where am I going wrong?

0 likes
8 replies
Snapey's avatar

This approach works well for me.

Create Impersonate Middleware;

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;

class Impersonation
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if(Auth::check() && Auth::user()->is_superAdmin && session()->has('impersonate')) {

            Auth::onceUsingID(session('impersonate'));

        }

        return $next($request);
    }
}

Add the middleware to the 'web' middleware group

All that is required for this to work is to store the user to be impersonated's ID in session

session()->put(['impersonate' => $user->id]);    // $user is the one you want to impersonate

To end impersonation, just clear the value from session.

An optional extra is a banner in the app layout blade view

@if(session()->has('impersonate'))
    <div style="background-color:#ffbb00;padding:6px;text-align:center;font-weight:bold;">You are impersonating this user.</div>
@endif
1 like
whoisthisstud's avatar

@snapey, I had actually tried this middleware first and it doesn't work for me. Once redirected, any link I click on from there redirects me back to login. Is it possible it has something to do with the one Livewire component that polls the server every X seconds?

Here is the middleware implemented:

//  Impersonate Middleware

namespace App\Http\Middleware;

use Auth;
use Closure;
use Illuminate\Http\Request;

class Impersonate
{
    public function handle(Request $request, Closure $next)
    {
        if( Auth::check() 
            // Tested w/ & w/o the following line verifying user role
            && Auth::user()->hasAnyRole(['SuperAdmin','Admin'])
            && session()->has('impersonate')
        ) {
            Auth::onceUsingID( session('impersonate') );
        }

        return $next($request);
    }
}

// Kernel.php

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Laravel\Jetstream\Http\Middleware\AuthenticateSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \App\Http\Middleware\SetUserRoutePrefix::class,
        \App\Http\Middleware\UserOnline::class,
        \App\Http\Middleware\Impersonate::class,
    ],

    ...
];
public function switchUser($id)
{
    $impersonated = User::find($id);
        
    session()->put(['impersonate' => $impersonated->id]); 
    session()->save();

    return redirect()->route('dashboard');
}
// navigation-menu.blade.php

@if( session('user_is_switched') || session('impersonate') )
<div class="alert bg-red-600 text-white text-center py-3 leading-none flex flex-col md:flex-row justify-center">
   <div class="self-center">You are currently logged in as {{ ucwords(Auth::user()->name) ?? '... well that\'s weird - you\'re logged in as no one.' }}.</div>
   <a href="{{ route('user.restore') }}" class="mt-3 md:mt-0 md:ml-2 bg-white bg-opacity-25 font-bold text-sm tracking-tight px-2 py-1 rounded self-center">Restore Login</a>
</div>
@endif
Route::group(['prefix' => '{prefix}', 'middleware' => ['auth']], function () {

	// ...

    // Restore user to original user from impersonated user
    Route::get('/restore-user', 'RestoreSwitchedUser@restoreUser')->name('user.restore');

    // ...

});
public function restoreUser() 
    {
        session()->forget('impersonate');
        session()->save();

        return redirect()->back();
    }
Snapey's avatar

That all reads fine, and the code I showed is lifted directly from a project that also uses Livewire on many views without an issue.

Looking at your web middleware, these are actioned in order so if you have user specific things in there (such as SetUserRoutePrefix) then you should impersonate before these.

With this technique, the session is still the superadmin's, but we are switching user on every request

whoisthisstud's avatar

@snapey, turns out nothing was wrong with my original code. It was an issue with FastCGI caching.

Thanks for your help!

draskog's avatar

I discovered this after upgrading from Laravel 8 to Laravel 9 and Jetstream

you should update your Kernel.php

remove \Laravel\Jetstream\Http\Middleware\AuthenticateSession::class from $middlewareGroups

// Kernel.php
 protected $middlewareGroups = [
        'web' => [
 		\App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
 	  ],

add 'auth.session' in $routeMiddleware

// Kernel.php

protected $routeMiddleware = [
        'auth'               => \App\Http\Middleware\Authenticate::class,
        'auth.basic'         => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'auth.session'       => \Illuminate\Session\Middleware\AuthenticateSession::class,
        'cache.headers'      => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can'                => \Illuminate\Auth\Middleware\Authorize::class,
        'guest'              => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm'   => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed'             => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle'           => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified'           => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];

then in Controller, you can use

class ImpersonationController extends Controller
{

    public function login(Request $request, User $user)
    {
        $originalId = auth()->user()->id;
        session()->put('impersonate', $originalId);

        Auth::loginUsingId($user->id, true);
        return redirect()->intended('dashboard');
    }

    public function logout()
    {
        if (!session()->has('impersonate')) {
            abort(403);
        }
        // login as the super user in session
        Auth::login(User::find(session('impersonate')));
        session()->forget('impersonate');

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

Hope this help.

Please or to participate in this conversation.