Admin Impersonation
Summary
Authenticated admins can impersonate pre-configured demo accounts to test the application as either a Pro or an Acheteur. The endpoint creates a real JWT access token and a refresh cookie for the demo account, exactly as if that user had logged in normally. Every impersonation is recorded in the audit log. The feature requires seed data (pnpm db:seed) to populate the demo accounts (demo-pro@sealion.fr, demo-acheteur@sealion.fr).
State Diagram
stateDiagram-v2 state "Validation du rôle" as Validating state "Rôle invalide" as Rejected state "Recherche compte Pro" as LookupPro state "Recherche compte Acheteur" as LookupAcheteur state "Compte démo introuvable" as NotFound state "Session émise" as TokenCreated [*] --> Validating: admin requests impersonation Validating --> Rejected: invalid role value Validating --> LookupPro: role == "pro" Validating --> LookupAcheteur: role == "acheteur" LookupPro --> NotFound: demo-pro@sealion.fr not in DB LookupAcheteur --> NotFound: demo-acheteur@sealion.fr not in DB LookupPro --> TokenCreated: signAccessToken + signRefreshToken + writeAuditLog LookupAcheteur --> TokenCreated: signAccessToken + signRefreshToken + writeAuditLog TokenCreated --> [*]: accessToken + httpOnly cookie returned
Steps
1. Admin Authentication (Actor: system)
authenticateAdmin preHandler validates the admin JWT. request.adminId is populated.
2. Validate Role (Actor: system)
role from request body must be one of "pro" or "acheteur". Returns 400 INVALID_ROLE with the valid options if not.
3. Look Up Demo Account (Actor: system)
Based on role:
"pro": findsprisma.pro.findUnique({ where: { email: "demo-pro@sealion.fr" } })"acheteur": findsprisma.acheteur.findUnique({ where: { email: "demo-acheteur@sealion.fr" } })
If not found: 404 DEMO_ACCOUNT_NOT_FOUND with a message instructing to run pnpm db:seed.
4. Issue Tokens and Write Audit Log (Actor: system)
Prisma $transaction:
- Create a refresh token record (
proRefreshTokenoracheteurRefreshToken) with asignRefreshToken()opaque 96-char hex, SHA-256 hashed, 7-day expiry. writeAuditLog()withaction: "pro.impersonate"or"acheteur.impersonate",actorId: request.adminId,targetId: demo account ID.
Access token: signAccessToken(userId, jwtSecret) — 15-minute HS256.
The refresh token cookie (proRefreshToken or acheteurRefreshToken) is set as httpOnly, secure (non-dev), SameSite lax, 7-day maxAge.
5. Response (Actor: system)
Returns { accessToken, role, identity: { id, email, firstName, lastName } }. The admin-side UI can now redirect to the Pro or Acheteur interface using the returned access token.
Error States
- Invalid role → 400
INVALID_ROLE - Demo account not seeded → 404
DEMO_ACCOUNT_NOT_FOUND
Security Notes
- Impersonation only targets hardcoded demo accounts — it is not possible to impersonate arbitrary users via this endpoint.
- All impersonation events are audited in the DB with actor and target IDs.
- The created session is real and subject to the same token TTLs as a normal login.
Related Processes
- No upstream process required. Seed data must exist.
- acheteur-analytics — admin can navigate to the acheteur’s account after impersonating to verify behaviour