lab404/laravel-impersonate
Laravel package that lets admins impersonate other users in a secure, reversible way. Start/stop impersonation via middleware and helpers, with session-based tracking and easy integration into your auth flow for debugging, support, and admin panels.
Installation:
composer require lab404/laravel-impersonate
The package auto-discovers and registers itself (Laravel 5.5+).
Publish Configuration (optional):
php artisan vendor:publish --provider="Lab404\Impersonate\ImpersonateServiceProvider"
This creates config/impersonate.php for customization (e.g., guards, middleware).
Add Routes (in routes/web.php):
Route::middleware(['auth', 'can:impersonate'])->group(function () {
Route::get('/impersonate/{user}', [\Lab404\Impersonate\Http\Controllers\ImpersonateController::class, 'impersonate']);
Route::get('/leave-impersonation', [\Lab404\Impersonate\Http\Controllers\ImpersonateController::class, 'leave']);
});
First Use Case:
/impersonate/3 (replace 3 with a user ID)./leave-impersonation to return to your admin session.@impersonatable and @canImpersonate directives in views to show/hide impersonation UI conditionally.\Lab404\Impersonate\Middleware\Impersonate to add custom logic (e.g., log impersonation events).Debug a User-Specific Bug:
/impersonate/{user_id}.Test Role-Based Features:
// In a controller or command
use Lab404\Impersonate\Facades\Impersonate;
// Impersonate a user by ID
Impersonate::impersonate($userId);
// Impersonate a user model directly
Impersonate::impersonate($userModel);
// Leave impersonation
Impersonate::leave();
Restrict impersonation to specific roles (e.g., admins):
// app/Http/Middleware/CheckImpersonationPermission.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Lab404\Impersonate\Facades\Impersonate;
class CheckImpersonationPermission
{
public function handle(Request $request, Closure $next)
{
if (Impersonate::isImpersonating()) {
abort_if(!auth()->user()->isAdmin(), 403);
}
return $next($request);
}
}
Register in app/Http/Kernel.php:
protected $routeMiddleware = [
// ...
'can.impersonate' => \App\Http\Middleware\CheckImpersonationPermission::class,
];
Show impersonation UI only to authorized users:
@auth
@canImpersonate
<a href="/impersonate/{{ $user->id }}">Impersonate</a>
@endcanImpersonate
@impersonating
<a href="/leave-impersonation">Leave Impersonation</a>
@endimpersonating
@endauth
Configure for API and web guards in config/impersonate.php:
'guards' => [
'web' => [
'enabled' => true,
],
'api' => [
'enabled' => true,
],
],
Impersonate via a specific guard:
Impersonate::impersonate($userId, 'api');
Extend impersonation to work with non-Eloquent users:
// app/Providers/ImpersonateServiceProvider.php
use Lab404\Impersonate\ImpersonateManager;
public function boot()
{
$this->app->extend(ImpersonateManager::class, function ($app, $manager) {
$manager->extend('custom_guard', function ($app) {
return new class($app['auth']) {
public function findUserById($userId)
{
return CustomUser::find($userId); // Your custom logic
}
};
});
return $manager;
});
}
Log impersonation sessions:
// app/Providers/EventServiceProvider.php
protected $listen = [
\Lab404\Impersonate\Events\ImpersonateStarted::class => [
\App\Listeners\LogImpersonation::class,
],
\Lab404\Impersonate\Events\ImpersonateEnded::class => [
\App\Listeners\LogImpersonation::class,
],
];
Redirect users after leaving impersonation:
// config/impersonate.php
'redirect_after_leave' => 'dashboard',
Or dynamically:
Impersonate::leave('/custom-redirect?user=' . $userId);
Test impersonation logic in PHPUnit:
public function testImpersonation()
{
$user = User::factory()->create();
Impersonate::impersonate($user);
$this->assertTrue(Impersonate::isImpersonating());
$this->assertEquals($user->id, auth()->id());
Impersonate::leave();
$this->assertFalse(Impersonate::isImpersonating());
}
Session Leaks:
auth()->user() in service containers).Impersonate::leave() in a finally block or middleware to ensure cleanup:
try {
Impersonate::impersonate($user);
// Your logic
} finally {
Impersonate::leave();
}
Multi-Guard Conflicts:
web and api) can cause auth state mismatches.Impersonate::impersonate($userId, 'api');
Blade Caching:
@impersonating directives may not update if Blade views are cached.@once directives:
@once
@impersonating
<!-- Dynamic content -->
@endimpersonating
@endonce
User Model Requirements:
getAuthIdentifier() on user models. Custom models may fail.Illuminate\Contracts\Auth\Authenticatable:
use Illuminate\Contracts\Auth\Authenticatable;
class User implements Authenticatable {
public function getAuthIdentifierName() { ... }
public function getAuthIdentifier() { ... }
// ...
}
Middleware Order:
auth may interfere with impersonation checks.auth in app/Http/Kernel.php:
protected $middleware = [
// ...
\App\Http\Middleware\CheckImpersonationPermission::class,
];
Check Impersonation State:
dd(Impersonate::isImpersonating(), auth()->user());
Inspect Guard Configuration:
dd(config('impersonate.guards'));
Enable Debug Logging:
Add to config/impersonate.php:
'debug' => env('IMPERSONATE_DEBUG', false),
Logs impersonation events to storage/logs/laravel.log.
Verify User Provider:
If findUserById fails, check your guard’s user provider:
dd(auth()->guard('web')->provider());
How can I help you explore Laravel packages today?