Multi-tenancy
Collection access already scopes who you can impersonate. The plugin only manages the tenant selector cookie.
This plugin has no multiTenancy option. It does not stamp a tenant on impersonation-sessions, and it does not filter the switcher. @payloadcms/plugin-multi-tenant already does that through collection access.read.
Put this plugin after @payloadcms/plugin-multi-tenant (and after @10x-media/dual-session).
Access
The switcher lists users over the normal collection REST API. Start then loads the target with overrideAccess: false. A tenant operator cannot pick or start a user their read access hides. Guessed ids from another tenant return forbidden.
access.impersonate is still the host gate. Platform admins who can read every user can impersonate every user. A tenant operator stays limited to what the tenant plugin already allows. Example, using the multi-tenant helpers in your config, not this plugin:
import { getUserTenantIDs } from '@payloadcms/plugin-multi-tenant/utilities'
impersonation({
access: {
impersonate: ({ req }) =>
req.user?.roles?.includes('admin') === true ||
getUserTenantIDs(req.user).length > 0,
},
})Do not add impersonation-sessions to the multi-tenant collections map. The rows are a hidden audit of who acted as whom, including a platform admin switching into a tenant user. Registering that collection would hide those rows from the people who need them.
Tenant cookie
@payloadcms/plugin-multi-tenant stores the nav selection in payload-tenant. It hardcodes that name and ignores cookiePrefix, so the default here does too. The cookie is not a capability, but a swap must not keep the impersonator's selected tenant while req.user is the target.
So payload-tenant is the default entry in cookies.clearOnSwitch, and the plugin treats it like any other entry:
| Mode | Start | Exit |
|---|---|---|
| Swap | Expire it. The multi-tenant plugin then selects the target's tenant when they have exactly one, and none otherwise. | Expire it. The multi-tenant plugin picks the impersonator's tenant the same way. |
| Parallel | Leave it alone. The admin session did not change. | Leave it alone. |
Opt out by setting cookies.clearOnSwitch to a list that does not include payload-tenant.
What this is not
- A second tenant-scoped admin view (see audit-logs).
- A
scopeResolver(see analytics). - Preference isolation. Payload prefs stay on the user document.