xddesigners / silverstripe-impersonate
Lets Administrators or a designated group temporarily log in as another Member, with a safe, auditable way back to their own account.
Package info
github.com/xddesigners/silverstripe-impersonate
Type:silverstripe-vendormodule
pkg:composer/xddesigners/silverstripe-impersonate
Requires
- php: ^8.1
- silverstripe/framework: ^5.0 || ^6.0
Requires (Dev)
- phpunit/phpunit: ^9.5
- squizlabs/php_codesniffer: ^3.0
This package is auto-updated.
Last update: 2026-08-05 08:34:07 UTC
README
Lets Administrators — or members granted an impersonate permission, or in a designated Group — temporarily log in as another Member, with a safe, auditable way back to their own account.
Login (both becoming the target, and switching back) is always completed through SilverStripe's own
IdentityStore — this module never maintains a parallel session mechanism. While impersonating,
Security::getCurrentUser() genuinely returns the target Member, exactly as if they had logged in
themselves.
Installation
composer require xddesigners/silverstripe-impersonate
Who can impersonate
Three ways, checked on every request against the actual logged-in Member:
- ADMIN permission — always sufficient, regardless of anything below.
- The Impersonate other members permission (
IMPERSONATE_MEMBERS) — grantable to any Group under Security → Groups → Permissions (category "Impersonation"). - Membership of the Impersonators group (matched by
Code, defaultImpersonators).
Both #2 and #3 are set up for you: on dev/build the module auto-seeds the Impersonators group holding
that permission, so it exists in the CMS ready to drop members into — no manual group/permission setup
needed. (The group's Code is stored exactly as configured, since the module matches on it.) Both the
permission code and the group code are configurable — see Configuration.
Usage
POST /impersonate/start/{MemberID} (with SecurityID in the request body)
POST /impersonate/stop (with SecurityID in the request body)
Both require a valid SilverStripe CSRF token (SecurityToken) and a POST request — GET is rejected
outright, so neither action can be triggered by a crafted link or image tag.
In your own templates/frontend, check the current state via:
use XD\Impersonate\Service\ImpersonationService; if (ImpersonationService::isImpersonating()) { $originalAdmin = ImpersonationService::getOriginalMember(); // e.g. render a "Viewing as {name} — [Switch back]" banner }
This module doesn't render any banner itself — it only exposes the state, so it doesn't assume or interfere with whatever frontend stack a consuming site uses.
Safety model
Every one of these is a deliberate, tested design decision, not an afterthought:
- CSRF + POST-only on both actions. Neither
startnorstopcan be triggered by anything other than an intentional, same-origin form submission carrying a freshSecurityID. - Permission is re-checked on every single request, from the actual logged-in Member —
ImpersonationService::canImpersonate()never trusts anything the client claims. There's no cached "is admin" flag anywhere. - No nested impersonation. You cannot start impersonating a second person while already
impersonating someone else —
starthard-rejects if a session is already active. You always know exactly one original identity is being tracked at a time. - No privilege escalation via the target. Administrators are never a valid target — no one can
impersonate an ADMIN, regardless of who they are or any config flag (an absolute block, ahead of the rule
below). Beyond that, you can never impersonate a Member who is otherwise privileged — a holder of the
impersonate permission or a member of the Impersonators group — unless you are a genuine ADMIN and
the site has explicitly opted in via
allow_impersonating_privileged: true; a non-admin impersonator can never impersonate a privileged account, regardless of that flag. Sites can additionally mark any Group as off-limits with the "Exclude this group from impersonation" checkbox (added to every Group in Security > Groups) — members of a flagged group can never be impersonated, the no-code way to protect privileged / system groups. The built-in Administrators group is seeded with this on. Together these are the main defence against using the feature itself to escalate privileges. - Optional sudo-mode gate (
require_sudo_mode, default on) — requires a recent re-authentication (SilverStripe's own elevated-session check) before an impersonation session can begin, the same mechanism the framework already uses to gate other sensitive actions. - Auto-expiry. A background middleware checks every request; if an impersonation session has run
longer than
max_duration(default 30 minutes), it is force-reverted before that request is even handled. A forgotten browser tab can't leave a live impersonation session open indefinitely. - All state lives server-side, in the PHP session only — nothing this module tracks is ever serialised to the client (no cookie payload, no JS-readable value, no URL parameter). There is no session state here for a user to read or tamper with directly.
- Session ID is regenerated on every login/logout transition, because both are completed via
IdentityStore::logIn()/logOut()— the framework's own login mechanics, which already defend against session fixation. This module never bypasses that. - No persistent/"remember me" login, ever, for either the impersonated session or the reversion back to the original account — both are session-lifetime only.
- Immutable audit log. Every session (start, stop, and why it stopped — manual vs. expired) is
recorded in
ImpersonationLog.canCreate()/canEdit()/canDelete()always returnfalse, so the trail cannot be altered or removed via any CMS or API surface, including by an Administrator — only this module's own internal code writes to it.
Configuration
XD\Impersonate\Service\ImpersonationService: allowed_group_code: 'Impersonators' # Group (by Code) whose members may impersonate allowed_permission_code: 'IMPERSONATE_MEMBERS' # permission that grants impersonation; '' to disable allow_impersonating_privileged: false max_duration: 1800 # seconds XD\Impersonate\Control\ImpersonateController: require_sudo_mode: true
The permission is registered by ImpersonatePermissionProvider (auto-discovered) and the Impersonators
group is seeded by ImpersonatorsGroupExtension on Group — both keyed off the codes above, so overriding
a code renames the permission/group the module looks for and seeds. That same extension adds the
ExcludeFromImpersonation checkbox to every Group (see safety point 4) and seeds it on the Administrators
group. Run dev/build after installing so the new Group.ExcludeFromImpersonation column is created.