An audit entry saying “customer changed billing email” is wrong if a support agent made the change while impersonating them. Impersonation audit logs have to keep both identities: the actor, who actually performed the action, and the subject, whose account and permissions were used.
It’s a question developers raise often. In a Directus discussion about adding impersonation (opens in a new tab), a maintainer asked who “is the one that’s tracked in accountability and revisions?” One reply was simply “Both.” Getting there takes two connected kinds of evidence: why access was allowed, and what happened inside the application.
Access records and business events answer different questions
An access request shows who wanted access, and why. An approval records a decision. A session record shows when access started and ended. None of them proves that an invoice changed or an export completed. That evidence comes from the application that did it.
| Evidence | Answers | Can’t prove on its own |
|---|---|---|
| Access request | Who asked to access which customer, and why | Whether it was approved |
| Approval | Who approved which scope and duration | Whether a session started |
| Session history | When access started and ended | Which business operations happened |
| Application audit event | Who did what, for whom, with what outcome | Whether the access was properly approved |
Define audit events for consequential reads, changes, denials and admin operations, based on your risk policy. Not every click needs an event.
What Devora records. Devora keeps the access side: requests (with the requester’s reason, if given), approvals (including whether an admin self-approved), scope, session start and end, and a tamper-evident security audit shown on each session’s page in the dashboard. It does not see or log business actions inside your application. Those events are yours to emit.
Keep requester, approver and actor as separate fields. The person who approves a session isn’t necessarily the one who uses it. Use a reason category and a ticket reference rather than copying ticket text into logs.
Take identities from verified authentication
Providers expose the actor in different ways:
- Clerk adds an
actclaim to impersonated session tokens (opens in a new tab).act.subis the impersonator; the top-levelsubis the customer. - Auth0 Session Delegation records the actor’s
subin its delegation log events (opens in a new tab). - Better Auth’s admin plugin stores (opens in a new tab)
impersonatedByon the impersonated session. - WorkOS puts the impersonator’s email in the access token’s
actclaim and records the required reason on itssession.createdevent, according to its AuthKit impersonation docs (opens in a new tab). - Devora provider integrations (opens in a new tab) put verified context on each request: actor, subject, session ID, scope, expiry and authorization source. Your integration must take it from verified authentication state, never from browser headers or bodies.
Build your audit fields from that verified context. Map external staff IDs to stable internal references, and don’t assume every provider shares an ID namespace. Never log the token itself. An actor claim tells you who acted. It doesn’t give that person permission to skip the customer’s authorization or tenant checks.
For queued work, keep the initiating actor and subject separate from the worker’s service identity. Decide whether the job re-checks permission when it runs, and record “accepted” and “completed” as separate events, so a queued job doesn’t look finished before it runs.
Example impersonation audit log entry
The OWASP Logging Cheat Sheet (opens in a new tab) recommends recording when, where, who and what for each event, including the outcome, plus an interaction identifier that links related events. Here’s a minimal application audit event using that idea. It’s an example schema for your own application, not a Devora export format, and all values are fictional:
{
"event_id": "evt_008",
"occurred_at": "2026-08-12T09:12:00Z",
"actor_ref": "staff_agent_a",
"subject_ref": "user_amber",
"tenant_ref": "tenant_amber",
"support_session_ref": "sess_004",
"reason_category": "permission_issue",
"interaction_ref": "req_008",
"action": "report.view",
"resource_ref": "report_003",
"outcome": "allowed"
}
support_session_ref comes from the session ID in verified impersonation context, or a reference you map to it. Never use an auth cookie, bearer token or upload credential as a correlation key. Pseudonymous IDs still need access controls, because swapping a name for an ID doesn’t make an event anonymous.
Define your outcomes precisely: allowed, denied, failed, completed. An authorization decision that allowed a request doesn’t prove the operation succeeded. For changes, emit completion from the transaction or a durable outbox, not from a frontend success message.
Test that the evidence lines up
In staging, use a fictional Agent A, Customer Amber and a read-only session. These are the outcomes to check:
| Scenario | Access evidence | Expected application evidence | Failure to look for |
|---|---|---|---|
| Approved report read | Request, approval, active session | Actor A, subject Amber, allowed | Customer recorded as the actor |
| Attempted settings change | Read-only session | denied; nothing committed | Click logged as a successful update |
| Cross-tenant report request | Session targeting Amber | Tenant denial with correct attribution | Other tenant’s data in the log or response |
| Request after the session ended | Session end time | Authentication rejection | Successful business event after revocation |
| Audit event delivered twice | One logical event | One event, or an explicit duplicate marker | Retry looks like a second action |
| Audit destination unavailable | Access record exists | Durable retry or a defined failure | Operation succeeds and the evidence is silently lost |
When you reconcile, compare subject, actor, scope and timing, not just a matching ID. Flag business events with no matching session, and sessions with unexplained gaps. A request that fails authentication may have no verified actor, so record that rather than trusting identity fields the client supplied.
Keep sensitive data out of logs
Don’t log passwords, tokens, secrets, raw auth headers or full customer payloads. Avoid names, email addresses, payment details and ticket narratives. The OWASP cheat sheet also covers (opens in a new tab) excluding or masking sensitive data and protecting logs from tampering and unauthorized access.
Set retention for each kind of evidence, limit who can read it, and protect integrity with restricted writes and tamper detection. Sanitize untrusted strings before logging them. Restrict the table that maps references back to real identities more tightly than routine log search.
Session recording is a separate decision. In Devora, recording and the activity timeline (opens in a new tab) are optional and off by default, and custom timeline events keep only an action name, not arbitrary metadata. A recording adds visual context. It doesn’t replace server-side audit events, and it doesn’t make you compliant with anything on its own.
Evaluate the whole chain
Your auth provider and existing audit system may already cover this. Buying a support-access tool doesn’t remove the need to instrument your own business events.
Review the provider prerequisites (opens in a new tab), session cleanup requirements (opens in a new tab) and security responsibilities. Pair this guide with the multi-tenant impersonation checklist and assigned-customer authorization.
To evaluate Devora, create an account and run one staging session. Then reconcile its access record with one audit event from your application. Devora doesn’t offer a bulk export or API for access history today, so plan your correlation around the session ID in the verified context your backend receives.
Sources
- Directus discussion #19085: Implement impersonating users & roles in App & API (opens in a new tab)
- Clerk: Session tokens (opens in a new tab)
- Auth0: Session Delegation behavior and monitoring (opens in a new tab)
- Better Auth: Admin plugin (opens in a new tab)
- WorkOS: Impersonation (AuthKit) (opens in a new tab)
- Devora: Auth providers (opens in a new tab)
- OWASP Logging Cheat Sheet (opens in a new tab)
- Devora: Recording privacy & activity logs (opens in a new tab)
- Devora: Session lifecycle and cleanup (opens in a new tab)