bwrigley's avatar

Impersonator login

We've got three user types on our system, guest, customer and admin.

I'd like for admins to be able to proxy login as a customer (guests don't count) so that they can see exactly what the customer would see.

At the moment I'm thinking about simply logging the admin in as the customer and having a session variable impersonatedBy which is the admin's userID to be carried around the whole session and used to log any actions the admin takes etc.

Before I went ahead with that route, I was just wondering if there is a known 'best practise' with how this is done as I don't want to reinvent the wheel.

0 likes
7 replies
Snapey's avatar
Snapey
Best Answer
Level 122

This is what I do;

ImpersonateController.php

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\User;
use Session;

class ImpersonateController extends Controller
{
    public function store(Request $request)
    {

        $user = User::find($request->who);

        session()->put('impersonate', $user->id);

        Session::flash('success', "You are now impersonating {$user->name}");

        return redirect(route('home'));

    }

    public function destroy()
    {
        session()->forget('impersonate');

        return redirect(route('home'));
    }
}


Middleware

<?php

namespace App\Http\Middleware;

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

class Impersonate
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if (session()->has('impersonate')) {
            Auth::onceUsingID(session('impersonate'));
        }

        return $next($request);
    }
}

register the route middleware

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

Show a button in the navbar

@if(session()->has('impersonate'))
         <li class="ml-4">
            <a class="btn btn-danger" href="#" onclick="event.preventDefault();document.getElementById('impersonate').submit();">
                <i class="fas fa-user-ninja mr-2"></i>Stop Impersonating
            </a>
        </li>

        <form method="POST" action="{{ route('admin.impersonate.destroy') }}" id="impersonate"  >
            {{ csrf_field() }}
            {{ method_field('DELETE') }}
        </form>

@endif

and then a couple of additional routes

inside the admin routes

    Route::post('admin/impersonate', 'Admin\ImpersonateController@store')->name('admin.impersonate.store');

The destroy method must be callable by the user you are impersonating so this goes inside authenticated routes

    Route::delete('admin/impersonate', 'Admin\ImpersonateController@destroy')->name('admin.impersonate.destroy');

edit: Sorry, missed a check from the navbar element. Added it now.

4 likes
bwrigley's avatar

@snapey this is a great implementation thank you. If you don't mind I shall steal it for mine. We have Sentinel but it should be relatively easy to swap out.

Just out of interest, in your example, when I am in impersonation mode, is there a way to access my real userid for logging and audit purposes or is this something separate you would store on the session?

raistie's avatar

Did you ever find a way to make it work with Sentinel? I can't seem to find a way to get it working because of Auth::onceUsingId

Snapey's avatar

I would store the original user in the session.

Its still available right before using the Auth:onceUsingId because your session cookie is still pointing to the original user. You could use it there, but I guess you want it when the actions are actually being performed.

1 like
bwrigley's avatar

yeah that sounds like a good plan. Thanks again!

Please or to participate in this conversation.