Impersonation Routes
Impersonation Routes
Allow admins to temporarily act as another user for support and debugging. Starting a session returns an opaque session token (64-char hex, not a JWT) valid for 1 hour; subsequent status/stop calls identify the session via the x-impersonation-token header. All three routes require the impersonation:use permission.
Routes
| Method | Path | Permission | Description |
|---|---|---|---|
| POST | /api/admin/impersonation/start | impersonation:use | Start impersonating a user |
| POST | /api/admin/impersonation/stop | impersonation:use | Stop impersonating |
| GET | /api/admin/impersonation/status | impersonation:use | Current session status |
Sessions are persisted in the ImpersonationSession Prisma model; without that model the routes degrade gracefully (start returns an in-memory session, stop/status become no-ops).
POST /api/admin/impersonation/start
Start an impersonation session for a target user.
Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
userId | string | Yes | ID of the user to impersonate. |
Response
{
"success": true,
"data": {
"sessionId": "imp_9f2c...",
"impersonatedUser": { "id": "usr_123", "name": "Alice", "email": "alice@example.com", "role": "user" },
"originalAdmin": { "id": "usr_admin", "name": "Admin", "email": "admin@example.com" },
"startedAt": "2026-07-27T12:00:00.000Z",
"expiresAt": "2026-07-27T13:00:00.000Z",
"token": "a1b2c3..."
}
}
Errors
- 401 — no authenticated admin on the request (
request.user.idmissing). - 403 — the target user has role
adminorsuper_admin(privilege-escalation guard). - 404 — target user not found.
POST /api/admin/impersonation/stop
End a session. The token is read from the x-impersonation-token header first, falling back to a sessionToken body field.
Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
sessionToken | string | No | Session token (alternative to the header). |
Errors
- 400 — no token provided in header or body.
- 404 — session not found.
The response reports sessionId, duration (seconds), actionsPerformed, and endedAt.
GET /api/admin/impersonation/status
Report the status of the session identified by the x-impersonation-token header. Returns { "isImpersonating": false } when the header is absent, the session is unknown, ended, or expired. Active sessions include sessionId, impersonatedUser, originalAdmin, startedAt, expiresAt, and timeRemaining (seconds).
Example — Full impersonation workflow
// 1. Start impersonation
const { data } = await fetch("/api/admin/impersonation/start", {
method: "POST",
headers: {
Authorization: `Bearer ${adminToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ userId: targetUserId }),
}).then((r) => r.json());
// 2. Check session status
const status = await fetch("/api/admin/impersonation/status", {
headers: {
Authorization: `Bearer ${adminToken}`,
"x-impersonation-token": data.token,
},
}).then((r) => r.json());
// 3. End the session when done
await fetch("/api/admin/impersonation/stop", {
method: "POST",
headers: {
Authorization: `Bearer ${adminToken}`,
"x-impersonation-token": data.token,
},
});
Start and stop events are recorded in the Audit Log.
AI Context
package: "@xenterprises/fastify-xadmin"
routes:
- POST /api/admin/impersonation/start — body: userId (required); returns 1h session token; 401 no admin, 403 admin target, 404 unknown user
- POST /api/admin/impersonation/stop — token via x-impersonation-token header or sessionToken body; 400 no token, 404 unknown session
- GET /api/admin/impersonation/status — reads x-impersonation-token header; { isImpersonating, ... }
permission: impersonation:use on all three
security: admin/super_admin users cannot be impersonated; tokens are opaque (not JWT), 1h TTL, non-renewable
notes: ImpersonationSession model optional — routes degrade gracefully without it
See Also
- Users Routes — look up the user ID to impersonate
- Audit Log Routes — all impersonation events are logged
