j84115/impersonate
Simple Laravel package to temporarily impersonate other users via /impersonate/login/{user_id} and /impersonate/logout. Add service provider, implement ImpersonateUser on your User model to define who can impersonate and who can be impersonated, then register Route::impersonate().
## Getting Started
### Minimal Setup
1. **Installation**: Manually clone the repo into `vendor/j84115/impersonate` and update `composer.json` autoload.
2. **Service Provider**: Register `J84115\Impersonate\ImpersonateServiceProvider` in `config/app.php`.
3. **User Model**: Extend your `User` model with `ImpersonateUser` interface and implement `impersonator()`/`impersonatable()` methods.
4. **Routing**: Add `Route::impersonate()` to `routes/web.php` (ensure it’s behind `auth` middleware).
### First Use Case
- **Impersonate**: Visit `/impersonate/login/{user_id}` as an admin (e.g., `/impersonate/login/5`).
- **Verify**: Check if the session now reflects the impersonated user’s data (e.g., `auth()->user()`).
- **Exit**: Visit `/impersonate/logout` to revert to the original user.
---
## Implementation Patterns
### Workflows
1. **Admin Dashboard Integration**:
- Add a UI button/link to trigger impersonation (e.g., "Impersonate User" in a user list table).
- Example Blade:
```blade
<a href="{{ route('impersonate.login', ['user' => $user]) }}">Impersonate</a>
```
- Redirect back to the original page post-impersonation using `redirect()->intended()`.
2. **Conditional Logic**:
- Use middleware to restrict impersonation routes (e.g., `can:impersonate`).
- Example:
```php
Route::impersonate()->middleware(['auth', 'can:impersonate']);
```
3. **Session Management**:
- Store the original user’s ID in the session before impersonating (package handles this internally, but you can extend via events).
- Listen for `impersonate.starting`/`impersonate.ended` events to log actions or update UI:
```php
Event::listen(J84115\Impersonate\Events\ImpersonateStarting::class, function ($event) {
\Log::info("Impersonating user {$event->user->id}");
});
```
4. **Testing**:
- Use `Impersonate::impersonateAs($user)` in PHPUnit tests to simulate impersonation:
```php
public function test_as_impersonated_user()
{
$admin = User::find(1);
$user = User::find(2);
Impersonate::impersonateAs($user);
$this->assertEquals($user->id, auth()->id());
}
```
### Integration Tips
- **Laravel Nova**: Override the user detail tool to include an "Impersonate" button.
- **APIs**: Extend the package to support API impersonation via tokens (custom middleware).
- **Notifications**: Trigger emails/notifications when impersonation starts/stops (e.g., for audit trails).
---
## Gotchas and Tips
### Pitfalls
1. **Permission Logic Errors**:
- Forgetting to implement `impersonator()`/`impersonatable()` methods will throw `MethodNotAllowedHttpException`.
- **Fix**: Double-check the interface implementation and test edge cases (e.g., `null` roles).
2. **Session Conflicts**:
- If using multiple auth drivers (e.g., API + web), impersonation may not persist across sessions.
- **Fix**: Ensure the same session driver is used (e.g., `config('session.driver')`).
3. **Route Caching**:
- After adding `Route::impersonate()`, run `php artisan route:clear` if routes aren’t detected.
4. **CSRF Issues**:
- The `/impersonate/login` route may fail if CSRF protection is enabled for POST requests.
- **Fix**: Exclude the route from CSRF verification in `VerifyCsrfToken` middleware:
```php
protected $except = [
'impersonate/login',
'impersonate/logout',
];
```
### Debugging
- **Check Events**: Listen for `ImpersonateStarting`/`ImpersonateEnded` events to debug flow:
```php
Event::listen(J84115\Impersonate\Events\ImpersonateStarting::class, function ($event) {
\Log::debug("Impersonating: " . $event->user->email);
});
dd(session()->all());
Custom Storage:
impersonate facade:
$this->app->bind(J84115\Impersonate\Facades\Impersonate::class, function ($app) {
return new CustomImpersonateService();
});
Rate Limiting:
/impersonate/login to prevent abuse:
Route::impersonate()->middleware(['throttle:5,1']);
Audit Logging:
ImpersonateUser interface to add logging methods:
public function logImpersonationStart(): void { /* ... */ }
public function logImpersonationEnd(): void { /* ... */ }
Multi-Tenancy:
impersonatable() checks tenant permissions:
public function impersonatable(): bool
{
return $this->email !== 'admin' && $this->tenant_id === auth()->user()->tenant_id;
}
impersonator()/impersonatable() methods./impersonate/login, /impersonate/logout). Override them by publishing the package’s routes (if supported in future versions) or use route model binding:
Route::post('/admin/impersonate/{user}', [ImpersonateController::class, 'login']);
How can I help you explore Laravel packages today?