Wednesday, July 17, 2019

Easily impersonate any user in a Laravel Application

Easily impersonate any user in a Laravel Application

EmailThis Premium lets you save unlimited bookmarks, PDF, DOCX files, PPTs and images. It also gives you a PDF copy of every page that you save. Upgrade to Premium →

Heads up: this article is over a year old. Some information might be out of date, as I don't always update older articles.

Introduction

Impersonate is quite a handy feature in some applications. It allows you to choose from a list of users and to impersonate them, in order to see the application from their point of view, without having to log out and log in again. For example, as an administrator you want to recreate a bug encountered by one of your users, without having them to share their password with you.

It's a functionality that it's really powerful, but at the same time it's easy to implement in Laravel. You just need to make sure that a normal user cannot impersonate an administrator.

Meet the impersonate Middleware

"HTTP middleware provide a convenient mechanism for filtering HTTP requests entering your application."

Middleware are additional layers that enclose the application logic, allowing modifications on the requests and responses of the application.

Middleware

For example, Laravel uses a middleware that verifies if the user of your application is authenticated. If not, the middleware will redirect the user to the login screen.

We can use the same logic to login as a different user for each request. Laravel provides the Auth::once() method to log a user into the application without sessions or cookies, however it requires an array of credentials, which we don't have. The solution is to use the Auth::onceUsingId(mixed $id) method, which serves the same purpose, but it requires only the user ID.

<?php  class Impersonate {     /**      * Handle an incoming request.      */     public function handle($request, Closure $next)     {         if($request->session()->has('impersonate'))         {             Auth::onceUsingId($request->session()->get('impersonate'));         }          return $next($request);     } } 

As you can see, if the session has an impersonate value, which contains the ID of the user that we want to impersonate, our middleware starts working, logging a different user for the request.

Next we have to register our new middleware to be available for our routes. You can add it into the $routeMiddleware array inside your App\Http\Kernel.php file.

<?php  protected $routeMiddleware = [     'auth' => \App\Http\Middleware\Authenticate::class,     'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,     'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,     'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,     'impersonate' => \App\Http\Middleware\Impersonate::class ]; 

Now we have to set up two routes in our application. One for impersonating a user and one for stopping the impersonation.

For example, in our UserController.php we can have something like this:

<?php  public function impersonate($id) {     $user = User::find($id);      // Guard against administrator impersonate     if(! $user->isAdministrator())     {     	Auth::user()->setImpersonating($user->id);     }     else     {     	flash()->error('Impersonate disabled for this user.');     }      return redirect()->back(); }  public function stopImpersonate() {     Auth::user()->stopImpersonating();      flash()->success('Welcome back!');      return redirect()->back(); } 

Notice: here I'm using the wonderful flash library developed by Jeffrey Way to flash notifications in the view.

Now we can declare these routes in our routes.php file

<?php  Route::get('/users/{id}/impersonate', 'UserController@impersonate'); Route::get('/users/stop', 'UserController@stopImpersonate'); 

or, if you prefer, you can use POST requests to avoid accidental calls to those routes. In every case make sure that the first route is accessible only to an authenticated user which has to be an administrator.

Now we have to add a couple of additional functions to the User model. As you can see, these are only descriptive methods that interact with the Session object.

<?php  class User extends Authenticatable {     public function setImpersonating($id)     {         Session::put('impersonate', $id);     }      public function stopImpersonating()     {         Session::forget('impersonate');     }      public function isImpersonating()     {         return Session::has('impersonate');     } } 

Crystal clear!

Finally you just need to add the middleware for the routes that you want to enable to impersonated users:

<?php  Route::group(['middleware' => 'impersonate'], function() {     // ... }); 

You can also use the helper function isImpersonating to add a little notification in the navbar, for example:

<ul class="nav navbar-nav navbar-right">     <li class="dropdown">         <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ Auth::user()->name }} <span class="caret"></span></a>         <ul class="dropdown-menu">         @if(Auth::user()->isImpersonating())             <li><a href="{{ action('UserController@stopImpersonate') }}">Stop Impersonate</a></li>         @endif             <li><a href="logout">Logout</a></li>         </ul>     </li> </ul>

Source: https://mauricius.dev/easily-impersonate-any-user-in-a-laravel-application/

Upgrade to Premium Plan

✔ Save unlimited bookmarks.

✔ Save PDFs, DOCX files, images and Excel sheets as email attachments.

✔ Get priority support and access to latest features.

Upgrade to Premium

1 comment:

Jennifer blog said...

This is very educational content and written well for a change. It's nice to see that some people still understand how to write a quality post تحويل الى pdf