Filament impersonate user action with exit banner
A Filament 4 table action that signs staff in as another user, records the original admin in the session and offers a one-click return.
FilamentAuthenticationfilamentimpersonationsupportauth
Code
<?php
namespace App\Filament\Actions;
use App\Models\User;
use Filament\Actions\Action;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\HtmlString;
class ImpersonateAction
{
public const SESSION_KEY = 'impersonator_id';
public static function make(): Action
{
return Action::make('impersonate')
->icon('heroicon-o-identification')
->requiresConfirmation()
->modalDescription('You will be signed in as this user until you click "Return to my account".')
// Hide it for yourself, for other admins, and while already impersonating.
->visible(fn (User $record): bool => auth()->user()?->role->value === 'admin'
&& $record->isNot(auth()->user())
&& $record->role->value !== 'admin'
&& ! session()->has(self::SESSION_KEY))
->action(function (User $record) {
$admin = auth()->user();
Log::info('impersonation.started', ['admin' => $admin->getKey(), 'target' => $record->getKey()]);
session()->put(self::SESSION_KEY, $admin->getKey());
Auth::login($record);
session()->regenerate();
return redirect('/account');
});
}
/** Render hook content: a red bar shown only while impersonating. */
public static function banner(): HtmlString
{
if (! session()->has(self::SESSION_KEY)) {
return new HtmlString('');
}
$name = e(auth()->user()?->name);
$url = e(route('impersonate.leave'));
return new HtmlString(
"<div style=\"background:#b91c1c;color:#fff;padding:.5rem 1rem;text-align:center\">"
."Viewing as {$name}. <a href=\"{$url}\" style=\"color:#fff;font-weight:600;text-decoration:underline\">Return to my account</a></div>"
);
}
}
<?php
namespace App\Http\Controllers;
use App\Filament\Actions\ImpersonateAction;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
// routes/web.php (must be reachable while signed in as the target user):
// Route::get('/impersonate/leave', StopImpersonatingController::class)
// ->middleware('auth')->name('impersonate.leave');
//
// Panel provider:
// ->renderHook(PanelsRenderHook::BODY_START, fn () => ImpersonateAction::banner())
class StopImpersonatingController extends Controller
{
public function __invoke(): RedirectResponse
{
$adminId = session()->pull(ImpersonateAction::SESSION_KEY);
abort_if($adminId === null, 404);
Auth::login(User::findOrFail($adminId));
session()->regenerate();
return redirect('/admin');
}
}
Needs: filament/filament@^4.0
How it works
What it solves
Support staff often need to see exactly what a customer sees. Asking for screenshots is slow, and sharing passwords is unacceptable. Impersonation signs the staff member in as the customer for a while and gives a safe way back.
How it works
ImpersonateAction::make() is a normal Filament table or page action. Its visible() closure is the safety net: only admins see it, never for themselves or for another admin, and never while an impersonation is already running. The modal confirms the intent.
On confirmation the action logs an audit line, stores the admin's id in the session under impersonator_id, calls Auth::login() for the target and regenerates the session id to avoid fixation. A redirect lands the admin in the customer area.
While the key exists, banner() returns a red bar through a panel render hook (the BODY_START hook). The "Return to my account" link hits a tiny invokable controller that pulls the id out of the session, logs the admin back in and sends them to the admin panel. Pulling the value makes the exit single use.
Adapting it
Replace the role check with a policy or permission such as can('impersonate', $record). Persist the audit trail in a table instead of the log if you need to prove who looked at what. Block sensitive actions, like password changes, while the session key is present.
Pitfalls
- The leave route must not require the admin role, because the current user is the target.
- Do not let impersonation chain: the visibility check prevents it.
- Billing and email side effects (for example "last login") should skip impersonated sessions.
How to use it
- Add
ImpersonateAction::make()to a user resource'srecordActions([...]). - Add the controller and register the
impersonate.leaveroute inroutes/web.phpwithauthmiddleware. - In your panel provider add
->renderHook(PanelsRenderHook::BODY_START, fn () => ImpersonateAction::banner())for each panel customers can enter. - Adjust the role names, redirect targets and the audit logging to your app.
- Test: impersonate a customer, confirm the banner, click Return.
Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.