Skip to main content
Petanque Life

Impersonation ("Login as")

F21.02 11 features

At a glance

The consent-gated, audit-saturated impersonation flow that lets a sys operator log in as a real user to reproduce bugs and assist with support. Every non-emergency session is bound to a `SupportAccessGrant` the user explicitly approves; break-glass is the only consent-skip path and is `sys_security`-only with two-admin TOTP and a 15-minute hard cap.

How it works

Impersonation cannot start without consent. The operator first opens a `SupportAccessGrant` bound to a support ticket, choosing one of three access levels (`view`, `interactive`, `full`) and an expiry. The target user is notified by email, in-app, and push, and approves or denies through `/me/support/access-grants`.

Only a `granted` row whose `requested_by` matches the operator unlocks `POST /auth/sys/impersonate`, which checks fresh-auth, the `sys_support` or `sys_engineer` capability, and that `access_level_requested <= grant.access_level`. The minted JWT carries both the operator's `actor_id` and the target's `sub` plus an `impersonation_id`, the `grant_id`, and the chosen access level. Every downstream capability check uses `sub` so the operator sees exactly what the target sees, while audit writes use `actor_id` so the trail always names the human behind the session.

A non-dismissable red banner pins to the top of every screen for the entire session: target name, email, reason, and an exit button, with a five-minute countdown when the 30-minute cap (or the grant expiry, whichever is shorter) approaches. Break-glass sessions show a dedicated badge, a 15-minute cap, and force-dispatch the post-hoc notification regardless of opt-out. Write guardrails are a hard server-side invariant: password, MFA, email, recovery code, self-role-grant, payment approval, legal acceptance, and account deletion all return `403 impersonation_write_blocked` at every access level. `view` rejects all non-GET with `403 grant_view_only`.

Revocation propagates within two seconds via a Redis flag (`401 grant_revoked`). Concurrency is capped at one session per operator, one per target, and one per grant. A sandbox-only sibling flow `POST /auth/sys/test-session` spawns a disposable role-typed session for feature verification without touching real users.

The user can review their own history through `GET /me/impersonation-history`.

Key capabilities

  • Consent-gated entry via `SupportAccessGrant` (pending → granted/denied → used/revoked/expired)
  • Three access levels: view (read-only), interactive, full; level cannot exceed grant level
  • Hard write guardrails on credentials, money, legal, role grants regardless of capability
  • Persistent non-dismissable red banner with reason, target identity, and countdown
  • 30-minute consent cap, 15-minute break-glass cap, Redis revocation within 2s
  • Break-glass requires `sys_security`, two-admin TOTP, forces notification past opt-out
  • Sibling `test-session` spawns sandbox roles for feature verification without real users
  • User-facing `/me/impersonation-history` plus per-grant audit trail

In practice

A federation admin reports that her invoice list is broken. The support operator opens a ticket, requests a `SupportAccessGrant` at access level `interactive` for two hours, and writes the reason. The user receives an email, in-app banner, and push, taps approve, and the operator's grant inbox flips to `granted`.

He opens the user-directory entry, clicks `Impersonate`, completes the fresh-auth passkey, and lands in the user's account with a red banner across the top. He reproduces the bug, files a fix, and clicks `Exit`; the grant flips to `used`, the user gets a post-hoc notification, and the audit trail records both `actor_id` and `sub` along with every endpoint touched. When a P0 outage later requires emergency access, the on-call security engineer triggers break-glass, a second admin confirms via TOTP, and the 15-minute capped session opens with the target user notified immediately rather than after the fact.

Features in this subsystem

11
ID Status Features
F21.02.01 Shipped Start impersonation bound to a SupportAccessGrant. Contract: { grant_id, access_level_requested }. Requires fresh-auth + sys_support / sys_engineer; access_level_requested ≤ grant.access_level; operator == grant.requested_by. ✅ PL-T122 + PL-T142
F21.02.02 Shipped Impersonation JWT carries both actor_id (sys operator) and sub (target user), plus impersonation_id UUID, grant_id, and chosen access_level linking to the audit trail. All downstream capability checks use sub; audit uses actor_id. ✅ PL-T122 + PL-T142
F21.02.03 Shipped Persistent banner across the entire screen — red background, fixed top, "Impersonating {name} ({email}) — {reason} — Exit". Non-dismissable. Break-glass banner shows a dedicated badge + shorter countdown. ✅ PL-T122 + PL-T142
F21.02.04 Shipped Max impersonation duration: 30 min (consent-gated, capped further by grant.expires_at); 15 min hard cap for break-glass. Auto-exit with banner countdown last 5 min. ✅ PL-T122 + PL-T142
F21.02.05 Shipped Write guardrails (unchanged security invariant): password, MFA, email, recovery-code, self-role-grant, payment approval, legal-accept, account deletion — 403 impersonation_write_blocked at every access level. PL-T142 adds: access_level == "view" rejects all non-GET with 403 grant_view_only; Redis revocation flag yields 401 grant_revoked within 2 s. ✅ PL-T122 + PL-T142
F21.02.06 Shipped Read-full fidelity: impersonator sees EXACTLY what the target sees — same tenant filtering, same sensitive-field masking, same feature flags. No "elevated" view. Implemented (PL-T122)
F21.02.07 Shipped Exit impersonation one-click — banner button or ESC twice. Returns to sys session, captures end timestamp in audit; grant.status → used on first exit. ✅ PL-T122 + PL-T142
F21.02.08 Shipped Target user is notified post-hoc by email; consent-gated sessions honour the user's per-channel opt-out. Break-glass sessions force dispatch regardless of opt-out (PL-T142). ✅ PL-T122 + PL-T142
F21.02.09 Shipped Target user can view their own impersonation history: GET /me/impersonation-history — list of times they were impersonated, by whom, with reason. ✅ PL-T122
F21.02.10 Shipped Concurrent impersonation limit: 1 per sys operator, 1 per target user, and 1 per grant (PL-T142). Second attempts surface 409 impersonation_already_active / 409 target_already_impersonated / 409 grant_already_in_use. ✅ PL-T122 + PL-T142
F21.02.11 Shipped "Login as self in role X" sibling flow — spawn a disposable test session in sandbox tenant with selected role (player/referee/club_admin/federation_admin). Used for feature verification without touching real user data. ✅ PL-T122b