victoryoalli/laravel-multitenancy-impersonate
Laravel package for impersonating users in multi-tenant applications. Let admins act as a tenant user while keeping tenant context and isolation intact, with simple start/stop impersonation flows for support, debugging, and administration.
The package lets an authenticated user (typically a central/super-admin) log in as another user — including a user that lives in a different tenant database and on a different domain — then return to their own session. v2.3.0 adds no runtime code; it ships an AI assistant skill, so existing installs upgrade with composer update victoryoalli/laravel-multitenancy-impersonate and nothing else.
composer require victoryoalli/laravel-multitenancy-impersonate
php artisan vendor:publish --tag="multitenancy-impersonate-config"
php artisan vendor:publish --tag="multitenancy-impersonate-migrations"
php artisan migrate
The migration creates the impersonation token table. Confirm which connection it runs on — see Gotchas.config/multitenancy-impersonate.php). Almost every question you will have (which user model, which guard, token lifetime, route/middleware names, rate-limit keys) is answered there, not in the README.resources/boost/skills/laravel-multitenancy-impersonate-development/SKILL.md, delivered from vendor/victoryoalli/laravel-multitenancy-impersonate/resources/boost/skills/. If you use Laravel Boost (or any editor that reads .ai/skills/), re-run Boost's install/sync so the skill is copied into your project, commit it, and point your agent at it. It is the most accurate description of CanImpersonate, ImpersonateToken, config keys, rate limiting, security and testing available to a model, and it beats letting the agent guess at config key names.User model: implement/use the CanImpersonate contract or trait, and add an authorization gate (see below) so only real admins can start an impersonation.Trait/contract on the user model. Put CanImpersonate on whichever model represents the impersonator (usually the central User), not on the tenant user model. If your central and tenant users are the same class, guard the capability with a role/permission check rather than the trait alone.
The token handoff, not a session copy. Cross-domain impersonation cannot share a session cookie, which is why the package mints an ImpersonateToken and redirects with it. Day-to-day this means: generate the token in the central context, redirect to the tenant URL, and let the package's middleware consume the token once and log the user in on the tenant guard. Treat the token as a short-lived, single-use credential — the same class of secret as a password-reset link.
Tenant bootstrapping order. With stancl/tenancy (or similar), identify and initialize the tenant before the token is validated and the user is resolved. The usual shape:
Route::middleware(['web', InitializeTenancyByDomain::class, PreventAccessFromCentralDomains::class])
->group(base_path('routes/tenant.php'));
The package's impersonation routes/middleware need to sit on the tenant side for consumption but be reachable from the central side for the redirect. Follow the config's route registration rather than hand-rolling URLs.
Rate limiting. Configure throttling in the package config and back it with a shared cache store (Redis, database). This is the main defence against token brute-forcing, so do not leave it on array or per-tenant file cache.
Audit trail. Whatever you do, log it: impersonator id, target id, tenant id, timestamp, IP. If the package emits events or notifications, listen for them in an EventServiceProvider; otherwise wrap the redirect in your own controller action so you have a single place to record intent.
Testing patterns. Feature-test both halves of the flow: an authorized admin receives a redirect containing a token; visiting the token URL authenticates as the target user ($this->assertAuthenticatedAs($target)); a second use of the same token fails; an expired token fails; an unauthorized user gets 403. Freeze time ($this->travel()) rather than sleeping for TTL tests. For the redirect assertions, use assertRedirectContains() instead of exact URL matching, since the token is random.
AI-assisted workflow (new in 2.3.0). With the skill installed, ask the agent to draft the gate, the leave-impersonation controller, and the tests against the skill's documented key names, then diff its output against config/multitenancy-impersonate.php. It is reliable for shape (which trait, which model, which middleware) and needs verification for values (your app's guard and connection names).
impersonate_tokens table must live somewhere readable before tenant initialization — i.e. the central connection. If the migration ran inside the tenant context, tokens are written to a tenant database and the consuming request (which has not yet initialized that tenant) will never find them. Symptom: a valid-looking redirect that lands on the login page with no error.auth()->guard(config('multitenancy-impersonate.guard')) in a tinker session inside a tenant context.ImpersonateToken supports Prunable, add it to the model:prune schedule; if not, add your own daily command.session('impersonator_id')) is on the central domain's cookie and will not be visible on the tenant domain. Restore the original session only after the redirect back to the central domain, keyed by something server-side.php artisan config:cache after editing the config; in tests use config()->set() in setUp() rather than editing the published file. Older tutorials reference key names that may not exist in your installed version — trust the shipped config file.ImpersonateToken model for a subclass if you need extra columns (reason, ticket id) for auditing, and override the routes if you need a policy check inside the controller.How can I help you explore Laravel packages today?