Building a Safe Support Impersonation Mode
Support staff eventually need to see what a customer sees, and the way that need is met determines whether you have a controlled, audited capability or a standing cross-tenant backdoor that an attacker only has to find once. This page is part of the multi-tenant authorization and data isolation guide.
Why a Permanent Cross-Tenant Role Is the Wrong Answer
The quickest implementation is a role that can read any tenant. It solves the support problem and creates a worse one: a set of accounts that permanently hold the highest-value access in the system, are used daily, and are indistinguishable from an attacker who compromises one of them.
The Shape of a Safe Grant
An impersonation session should be requested, scoped, limited and expiring.
Requested means a support engineer names the account and gives a reason, usually linked to a ticket. That reason is what makes the audit trail readable months later, and requiring it costs seconds.
Scoped means the grant covers one tenant, or one account within it, rather than a general capability. A grant is a row with a subject, a target, a reason, an expiry and — for sensitive tenants — an approver.
Limited means the impersonated session cannot do everything the customer can. Reading data to diagnose a problem is the common case; changing security settings, exporting in bulk, deleting, or performing payment actions should be denied outright rather than merely discouraged, because those are the operations an attacker would want and the ones a customer would most object to.
Expiring means the grant ends on its own — thirty to sixty minutes is usually plenty — with an explicit extension if the work is not finished. Nothing that requires a human to remember to revoke will reliably be revoked.
export async function startImpersonation(actor: User, targetUserId: string, reason: string) {
const target = await users.get(targetUserId);
await requireApprovalIfSensitive(actor, target.tenantId);
const session = await sessions.create({
userId: target.id,
tenantId: target.tenantId,
impersonatedBy: actor.id, // never lost — every audit event carries it
capabilities: SUPPORT_READ_ONLY, // a reduced set, not the customer's own
absoluteExpiry: Date.now() + 30 * 60_000,
});
await audit.record("impersonation.started", { actor: actor.id, target: target.id, reason });
await notify.customer(target.tenantId, "support_access_started", { actor: actor.displayName, reason });
return session;
}
Keeping the Real Actor Visible
The most important property is that the system never forgets who is really acting. Every audit event produced during an impersonated session must carry both identities: the customer being acted as, and the staff member acting.
Show it in the interface too. A persistent banner naming the customer being impersonated prevents the genuinely common mistake of a support engineer forgetting which account they are in and making a change in the wrong place. It also makes screenshots unambiguous when they end up in a ticket.
Customer Visibility
Decide what the customer is told, and prefer more rather than less. Many contracts and several regulatory regimes require notification; even where nothing requires it, telling customers that support accessed their account — with who, when and why — converts a source of suspicion into evidence of good practice.
Offer an account-level setting for customers who want to approve access rather than merely be told, and honour it. Enterprise buyers ask for this specifically, and having it already built is far easier than retrofitting an approval flow into a mechanism support staff use daily.
Keep the history visible to the customer, not just to you. A list of support sessions with timestamps and reasons in their own admin area is a small feature that answers a large category of questions without a support conversation.
Entering and Leaving Cleanly
The transitions are where impersonation implementations leak. Entering must mint a new session rather than mutating the existing one, and leaving must restore the staff member’s own session rather than simply clearing a flag.
Mint a fresh session identifier on entry, exactly as you would at any other privilege change, and record the staff member’s original session so that ending impersonation returns them to it. A flag toggled on a single session is fragile: any code path that reads the session without checking the flag sees a customer session belonging to a staff member, which is precisely the confusion you were trying to avoid.
End the session automatically on expiry, on explicit exit, and on logout — and make sure the exit path cannot leave a half-state where the capabilities have been reduced but the identity has not been restored. Test all three exits, because the automatic one is the least exercised and the most likely to be wrong.
Reviewing the Trail
An audit trail nobody reads is storage. Make impersonation reviewable by building the report before you need it: sessions per staff member per week, sessions per customer, sessions without a linked ticket, and sessions that performed writes.
Those four views turn a pile of events into questions with answers. A staff member whose usage is an order of magnitude above their peers is worth a conversation — usually about a workflow that could be replaced with better tooling, occasionally about something else. Sessions without a reason are a process gap. Sessions with writes deserve a routine second look.
Give the report an owner outside the support organisation. A team reviewing its own access data will optimise for the metrics it is shown, which is human and unhelpful; a security or compliance owner asking questions from outside produces better conversations and satisfies the segregation-of-duties expectation that auditors will raise anyway.
Retain the events for as long as your longest obligation, in storage the application can append to but not rewrite, alongside the rest of your access trail. When a customer asks “did anyone at your company look at our data in March?”, the answer should be a query rather than an investigation, and it should be the same query every time.
Sample a few sessions in each review and read them end to end rather than only scanning the counts. Reading what a session actually did is how you discover that a workflow requires impersonation when it should not, or that a diagnostic step could be replaced by a customer-visible tool — both of which reduce future usage far more than any policy.
Set a review cadence and stick to it. Monthly is enough for most products, and the value is as much in the tooling improvements it prompts as in the anomalies it catches.
Frequently Asked Questions
Should impersonation allow writes?
Read-only by default, with a narrow, separately approved write mode for cases where reproducing a fix is genuinely necessary. Most support work is diagnostic, and read-only removes the possibility of a support session changing something nobody can later explain. When writes are permitted, log them distinctly so a review can separate them from the customer’s own actions immediately.
How long should a session last?
Thirty to sixty minutes, with an explicit extension that is itself logged. Longer sessions drift into being permanent access with extra steps, and the expiry is the control that stops a forgotten tab from being an open door into a customer’s data overnight.
Does the customer have to approve?
That depends on your contracts and the sensitivity of the data. A reasonable default is notify-always, approve-for-sensitive-tenants, with the approval requirement configurable per customer. What matters is that the choice is deliberate and documented rather than an accident of what was easiest to build.
Should impersonated sessions count against session limits?
No. A support session should not evict the customer’s own session, and it should be excluded from any concurrent-session cap. Mark it with its own type so limits, device lists and security notifications can treat it correctly — showing it in the customer’s device list is good, evicting a real user because of it is not.
What if support needs access to investigate an outage affecting everyone?
That is a different capability and deserves a different mechanism: aggregate diagnostics, logs and metrics that do not require entering an individual account. Building good operational visibility is what keeps per-account impersonation rare, and rarity is what makes the audit trail meaningful — a mechanism used a hundred times a day stops being reviewed.
Done well, this capability is one of the easier things to explain to a security-conscious customer, because every property they would ask for — scope, expiry, approval, notification, audit — is something you can demonstrate rather than assert.
Related
- Multi-tenant authorization and data isolation — the boundary this capability deliberately crosses.
- Auditing permission changes with an append-only log — where impersonation events belong.
- Preventing session fixation and hijacking — regenerating identifiers when entering and leaving an impersonated session.