Impersonation session termination: expiry, revocation and open tabs

Impersonation session termination needs more than a stop button: expiry, server-side revocation, open tabs, provider APIs and a staging test table.

Impersonation session termination means making sure a support session’s access actually stops: the credential fails on your server, any open tab cleans up, and the record shows who ended it and why. The common mistake is treating the “stop impersonating” button as the whole job. A button only runs in a tab that’s still open.

There are three different events to handle. A session expires when its time limit passes. It’s revoked when someone ends it early, on purpose. And browser cleanup clears cookies or tokens in a tab that happens to be open. A safe design needs all three, and it relies on the server-side ones.

What does ending an impersonation session involve?

Ending a session safely means the server stops honoring the credential, whatever any browser is doing. Each mechanism covers different gaps:

MechanismWhat it stopsWhat it misses
Expiry on the credentialEvery use after the deadline, with no one actingEarly endings: a ticket closed, an approval revoked
Server-side revocationEvery request with that credential, tab open or notSystems that accept the credential without checking your revocation list
Browser cleanupThe current tab’s cookies, tokens and screenClosed tabs, other tabs, copied tokens

Set the expiry first, as the backstop. Then add revocation for early endings. Treat browser cleanup as good hygiene, not as the control.

Why isn’t a “stop impersonating” button enough?

A button only works in the tab where it’s clicked. Several common cases slip past it:

  • The agent closed the tab. No callback runs, and the credential stays valid until it expires.
  • Another tab is open. If your login is a shared cookie, a second tab or a reload is still signed in as the customer. In Devora’s frontend SDK (opens in a new tab), the per-tab session capability is never written to browser storage. Without a session bridge, a reload or new tab runs as the impersonated user with no banner, scope enforcement or capture.
  • The token is stateless. A signed token that your backend verifies without a lookup stays valid until it expires, whatever happens to the session behind it.
  • The session is cached. Caches in your auth layer can keep accepting a revoked session for a while.

So the reliable pattern is server-side: store the support session ID with the credential you issue, record that ID as revoked when the session ends, and reject any credential whose session ID is on that list. Keep the record at least until the credential would have expired anyway. Devora’s session lifecycle guide (opens in a new tab) uses the same pattern. It also catches a credential issued by a slow handler after the termination request arrived.

How do Clerk, Better Auth and WorkOS end impersonation?

Each provider has its own clocks and revocation calls.

Clerk. Revoking an actor token (opens in a new tab) only stops a token that hasn’t been used to sign in yet. Once the impersonated session exists, end it with the Backend API’s revoke-session endpoint (opens in a new tab), which sets the session to a revoked, unauthenticated state. Without that, the session ends at its maximum duration (opens in a new tab), 30 minutes by default, or after 10 minutes of inactivity. Clerk session tokens last 60 seconds (opens in a new tab), so a backend that verifies them without a network call can still accept one issued just before the revocation. Your own session-ID check closes that gap.

Better Auth. In the Better Auth admin plugin (opens in a new tab), stopImpersonating deletes the impersonated session and restores the admin’s session from a signed admin_session cookie (opens in a new tab). That only works from the browser holding the cookie. For server-side cleanup, revoke that one impersonated session by its token. The admin plugin’s revokeUserSession does this as an authorized admin call. Don’t use “revoke all sessions for a user”, which would sign the customer out everywhere. If you enable cookieCache, Better Auth’s session management docs (opens in a new tab) note that revoked sessions may stay active until the cache expires, so keep its maxAge short or disable it where immediate revocation matters.

WorkOS AuthKit. Impersonation sessions expire automatically (opens in a new tab) after 60 minutes. To end one sooner, call the revoke session endpoint (opens in a new tab), POST /user_management/sessions/revoke, with the session ID from the access token’s sid claim. WorkOS’s AuthKit sessions guide (opens in a new tab) recommends a short access token duration so that changes to a session reach your app quickly.

ProviderNative endEarly server-side endWatch out for
Clerk30-minute max, 10-minute idle by defaultRevoke the Clerk sessionRevoking the actor token doesn’t end a started session
Better Auth1 hour by default, or end of browser sessionrevokeUserSession with that session’s tokenstopImpersonating needs the admin_session cookie
WorkOS60 minutesRevoke session by sidAccess tokens issued before revocation

The provider guides go deeper: Clerk, Better Auth and WorkOS.

How does Devora handle impersonation session termination?

Every Devora session ends in Devora first. Devora then sends a signed DELETE termination request (opens in a new tab) to your backend, even when no browser tab is open, and tells any open tab through the frontend SDK. The request body carries a reason:

ReasonWhen it’s sent
user_endedThe agent ended the session from the banner or the dashboard
time_limitThe approved window ended, or the session was idle too long
admin_terminatedAn admin force-ended it, or the customer’s data was erased
request_revokedThe approval behind the session was revoked
membership_revoked, role_downgradedThe agent lost access in Devora
start_failed, not_startedA token may have been issued, but the session never became active

That’s a subset. Treat any reason you don’t recognize the same way: revoke.

Delivery is retried. Devora treats any 2xx as delivered. After a timeout, 5xx, 408, 425 or 429, it makes up to 5 attempts within 6 hours of the end. Any other 4xx stops delivery. Each attempt is a new signed request, so your handler must be idempotent. If delivery fails, the session page shows that your application wasn’t told, and an admin can retry the notice.

The backend guard (opens in a new tab) doesn’t wait for your handler. By default, it checks whether a session is still live and rejects ended sessions on every route it covers, caching the result for five seconds. Open tabs notice a server-side end on their next upload, request or session check, which runs every 60 seconds by default. Routes outside the guard, and systems that accept your credential directly, still depend on your revocation.

Your application owns the credential. Devora tells you when to revoke it, and your TERMINATE handler does the revoking. Then your onSessionEnd callback clears browser state in any open tab (opens in a new tab).

What happens to data recorded just before the end?

Access closes at the moment of the end. Recording and activity data captured before the end can still be in flight, so Devora accepts it if it arrives within 60 seconds of the end (opens in a new tab). That window never extends access, and there’s none after a data erasure. A page whose final upload still didn’t arrive, usually a background tab, shows Stopped at session end in the replay, with only the last few seconds missing.

Recording is optional (opens in a new tab) and off by default. If you don’t record, this section doesn’t affect you.

How do you test impersonation session termination in staging?

Use synthetic accounts and call your API directly. Check the server’s response, not the screen. These are pass conditions to check, not results we’ve run.

ScenarioPass conditionEvidence
Agent ends the session from the bannerNext API request with the credential is rejectedRequest ID and response
Admin ends it with the agent’s tab closedTermination request arrives; credential rejected afterwardsHandler log and a later rejected request
Session reaches its time limit with no tab openCredential rejected at expiryTimestamps of expiry and request
Second tab or reload after the endNo customer data loadsResponse from the second tab
Termination request delivered twiceHandler returns 2xx both times; one revocation recordTwo request IDs, one record
Token issued after termination (slow handler)Rejected by the session-ID checkRevocation record and denial
Customer’s own session during and afterUnaffectedCustomer session still valid
Provider session after the endRevoked in Clerk, Better Auth or WorkOSProvider session state

The read-only impersonation guide adds the scope tests. The multi-tenant checklist covers the rest of the session lifecycle.

If you’d like termination notices, liveness checks and access history handled for you, 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.

How do I end an impersonation session safely?

Revoke the credential on your server, keyed to the support session, so every request using it fails. Then clear browser state in any open tab and send the agent to a safe page. Keep a hard expiry on the credential as a backstop in case the revocation step never runs.

Does revoking a Clerk actor token end the impersonation?

No. Revoking an actor token only stops an unused token from signing in. Once the impersonated session exists, end it by revoking the Clerk session through the Backend API, and keep your own check for the support session in case a short-lived session token is still in use.

What does Better Auth's stopImpersonating do?

It deletes the impersonated session and restores the admin's session from a signed admin_session cookie. It only works from the browser that holds that cookie, so cleanup with no tab open has to revoke that specific impersonated session on the server, for example with the admin plugin's revokeUserSession.

What happens if the agent closes the tab before the session ends?

Browser callbacks can't run, so nothing in the browser cleans up. The credential stays valid until it expires unless your server revokes it. That's why the server-side termination step matters more than the stop button.

Does Devora's 60-second late-data window keep the session open?

No. Access closes at the end: the backend guard rejects the session and new actions are refused. Devora only accepts recording and activity data the tab captured before the end if it arrives within 60 seconds, and accepts none after a data erasure.

Put it into practice

Give your team context.Keep control of access.

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