Impersonation session isolation: keep support sessions separate across browser tabs

Impersonation session isolation keeps a customer session from leaking into new tabs and reloads. Use separate profiles, per-tab state and a clean end.

Impersonation session isolation means a support agent’s customer session stays where you put it: in one tab or one browser profile, always with a banner and the right restrictions, and it ends without leaving the agent signed in as the customer. Most apps don’t get this for free. Their login is a cookie, cookies are shared by every tab in a browser profile, and so a reload or a new tab quietly acts as the customer too.

The fix has three parts: keep staff and customer sessions in different places, make every tab prove it belongs to the support session before it renders, and end everything from the server.

Why does impersonation leak into other tabs?

Because different browser storage has different scope. Your auth cookie is shared across tabs, while anything you keep in a page’s memory or in sessionStorage is not.

MDN describes sessionStorage (opens in a new tab) as partitioned by origin and by tab, and notes that opening a page in a new tab creates a new session, “which differs from how session cookies work.”

Where state livesShared withWhat that means for impersonation
Cookie on your app’s hostEvery tab and window in the profileReloads and new tabs are signed in as the customer
localStorageEvery tab on the origin, until clearedSame as cookies, and it survives restarts
sessionStorageOne tab, but copied to a page opened from itMostly per tab; not a secret boundary
In-memory JavaScript stateOne page loadLost on reload; must be re-established

So the usual failure looks like this. Support opens the customer’s account in tab A, and a banner shows “Viewing as Amber, read-only”. They middle-click a link, and tab B loads the same app with the customer’s cookie but none of tab A’s in-memory state. Tab B has no banner, and any browser-side restrictions that lived in tab A’s memory aren’t there either.

What happens to the staff member’s own session?

It’s usually replaced in that browser, because one profile holds one login per site.

Clerk. Clerk’s user impersonation guide (opens in a new tab) says an actor token’s URL is a Clerk Frontend API URL that will “sign out existing users” and prepare sign-in for impersonation. If an agent who also uses your product as themselves opens it in their normal profile, they’re signed out of their own account.

Better Auth. The Better Auth admin plugin (opens in a new tab) creates a session that mimics the user. It stays active until the browser session ends or it reaches one hour by default, and stopImpersonating returns to the admin account. That restore only happens in the browser that runs it.

For agents who use your product as themselves, the simplest isolation is a separate browser profile just for support work. Chrome profiles keep browsing data such as history, passwords and settings separate, and Google’s Chrome help suggests (opens in a new tab) them for keeping different accounts, like work and personal, separate. A distinct profile color and name also make it obvious which window is acting as a customer.

Which impersonation session isolation option fits your team?

Pick based on how often support impersonates and whether staff also use the product as customers do.

OptionHow it isolatesChoose it whenWatch for
Separate browser profileSeparate cookie jar and storage per profileStaff use the product as themselves, or you want a quick operational fixIt’s a habit, not a control; nothing stops an agent using the wrong profile
Per-tab capability with server restoreEach tab must re-establish the support context before it rendersYou want reloads and new tabs to behave predictablyNeeds backend and frontend work
Separate support originHost-only cookies stay on that hostYou can serve the app on a dedicated hostnameAuth providers and redirects must support that host
Short-lived credential and server revocationLimits how long any leaked tab stays usefulAlwaysDoesn’t fix the missing banner on its own

The separate-origin option relies on host-only cookies. According to MDN’s Set-Cookie reference (opens in a new tab), a cookie set without a Domain attribute is only returned to the host that set it, not to subdomains. That can keep a support session off your main app host, but it’s a bigger change than most teams need.

These options stack. A separate profile is cheap and helps humans. Per-tab restore and server revocation are what make the system behave correctly when a human slips.

How do you keep per-tab state honest?

Treat each tab as unknown until it proves it belongs to the support session. Keep the capability out of persistent storage, have each new page load ask your server whether the logged-in session is impersonated, and render nothing protected until the answer is in.

A few rules make that work:

  • Keep the capability in memory. It disappears on reload, which is the point. Re-establish it through an authenticated call to your backend, which already knows from verified session state whether this login is an impersonation.
  • Don’t treat sessionStorage as a secret. A page with an opener starts with a copy of the opener’s sessionStorage, as MDN’s sessionStorage page (opens in a new tab) notes. It’s fine for a non-secret tab ID, not for a credential.
  • Fail closed in the browser. If a tab knows it’s impersonated but can’t restore the support context, show a safety screen, not the customer’s data without a banner.
  • Tell sibling tabs about lifecycle events. BroadcastChannel lets same-origin tabs, windows and frames message each other, per MDN’s BroadcastChannel docs (opens in a new tab). Use it so an end in one tab is reflected in the others quickly, and never send tokens or customer identity over it.
  • Keep the server authoritative. Browser coordination is a convenience. The backend still has to reject expired or revoked sessions on every request.

The banner deserves its own rule: every tab that shows customer data during a support session should show it. If a tab can’t show the banner, it shouldn’t show the data either.

Side effects follow the same logic. If your analytics suppression reads only browser state, an unrestored tab will track the customer as usual. Deriving the flag from the server session avoids that; see user impersonation side effects.

How does Devora handle new tabs and reloads?

A Devora session starts in the tab that opened the Devora link, and Devora’s per-tab capability is never written to browser storage. As Devora’s frontend integration guide (opens in a new tab) explains, your own login is shared with other tabs, so without a session bridge a reload or new tab runs as the impersonated user with no banner, scope enforcement or capture.

The session bridge closes that gap:

  1. You pass a sessionBridge option to the frontend SDK. It calls an authenticated route on your backend, which you mount with the backend SDK’s browser-session helper.
  2. That route, described in the Node SDK bridge reference (opens in a new tab), returns none for ordinary users without contacting Devora. For an impersonated session it returns a 30-second, single-use resume code bound to that tab and origin, or blocked when Devora can’t safely restore it.
  3. The tab redeems the code for its own capability, then starts scope enforcement and capture. A restored tab doesn’t call onImpersonate again, per the JavaScript SDK reference (opens in a new tab).

Until the bridge answers, and whenever it reports blocked, protected content must not render. The React and Solid providers handle this for you; in Vue and Svelte you wrap protected content in BridgeGuard. Sibling tabs are told over BroadcastChannel when a session ends, and Devora’s validation check remains authoritative, as the JS reference on restoring tabs (opens in a new tab) describes.

One clarification on “no scope enforcement”. That refers to Devora’s browser-side layers. The backend guard is the security boundary (opens in a new tab), and it still applies to the routes it protects, as long as your server session carries the verified Devora context. The bridge is what keeps the banner, browser guards and capture consistent across tabs.

How should an impersonation session end cleanly?

End it on the server first, then clean up every tab. Browser code only runs in tabs that are still open, so it can’t be the whole mechanism.

In Devora, onSessionEnd runs when the agent ends the session, the time limit is reached, or the session is ended in Devora or another tab. It should clear the impersonated auth state and send the browser to a safe signed-out or staff route. Devora’s frontend guidance is explicit (opens in a new tab): don’t silently fall back to the previously signed-in customer, and if your provider supports restoring the staff session, follow its documented flow.

The server side is a signed termination request (opens in a new tab) to your backend for every ending, retried if delivery fails and sent even with no tab open. Your handler revokes the credential by Devora session ID. The session termination guide covers revocation in detail, and the Clerk impersonation guide covers revoking a Clerk session.

Test isolation in staging

Use a staging customer, a staff account that also uses the product, and two browser profiles. These are pass conditions to check, not results we’ve run.

ScenarioExpected outcome
Open the impersonation link in the support profileStaff member’s main-profile session is unaffected
Reload the impersonated tabBanner and scope return, or a safety screen shows; never customer data without a banner
Open a link from the impersonated tab in a new tabSame as reload
Duplicate the impersonated tabNew tab gets its own capability or is blocked
Bridge route returns an errorProtected content doesn’t render
End the session in one tabOther tabs leave customer data and reach a safe page
End the session from the Devora dashboard with the tab in the backgroundNext request from that tab is rejected by the backend
Agent returns to their own accountSigned in as staff, not as the customer

If you want per-tab restore, the banner and server-side endings without building them, create a Devora account and try one staging session against this table.

Sources

Quick answers

Common questions, answered clearly.

Direct answers to the questions teams usually ask when evaluating this approach.

Why does a new tab open as the customer during impersonation?

Because your login is usually a cookie, and cookies belong to the browser profile, not the tab. Once a support session replaces the login, every tab and reload on that site sends the customer's session. Only state you deliberately keep per tab, such as an in-memory capability, stays in the original tab.

Should support staff use a separate browser profile for impersonation?

Yes, if they also use your product as themselves. Some providers sign out the existing session when an impersonation link is opened, so a dedicated profile keeps the staff member's own login and the support session apart. It also makes it obvious which window is acting as a customer.

Is it safe to store the impersonation token in sessionStorage?

It's better than localStorage, but it isn't a hard boundary. A page opened from another page can start with a copy of the opener's sessionStorage, and any script on the origin can read it. Keeping the capability in memory and re-establishing it through an authenticated server call is safer.

What should happen in other tabs when impersonation ends?

They should stop showing customer data, clear the impersonated login and move to a safe page. Notify sibling tabs right away, but rely on the server: revoke the credential so any tab that missed the notice gets rejected on its next request.

Does Devora keep impersonation in one tab?

The session starts in the tab that opened the Devora link, and its per-tab capability is never written to browser storage. Your own login is still shared, so you add a session bridge that lets each new tab or reload restore Devora's controls, or render nothing if it can't.

Put it into practice

Give your team context.Keep control of access.

Bring approvals, scoped sessions, and access history into your support workflow.