The one-line answer to what is active session management: the account page lists every device that is signed in, and one button signs all of them out. The 5 lines of the checklist below make that button true. It exists for the lost phone, where a changed password can leave the thief’s session working until its token expires.
What is active session management: a device list, revoke one, revoke all
Active session management is the part of an app that shows a user every device currently signed in to their account and lets them end one session or all of them. It sits on top of ordinary session handling and exists for the lost phone, the borrowed computer and the login nobody recognizes.
Session management in web technology is how a server remembers that a browser already signed in. MDN’s session management guide describes two models: the state kept in a database on the server with the client holding an identifier for it, or the state kept “as a digitally signed object in the client”, typically a JWT, and MDN’s most common answer to revoking one is a short-lived access token plus a refresh token. Active session management is the user-facing layer on top of either: show the sessions, end one, end all. It is one line of the authentication checklist, and the one a user needs when a phone goes missing. Proxy dashboards use the same phrase for their open connections, which is a different thing.
What the user sees for each session is short:
| What the list shows | Where it comes from | What it lets the user spot |
|---|---|---|
| Device and browser | The user agent recorded at sign-in | A phone or laptop they do not own |
| Rough location | The IP address, reduced to a city or region | A sign-in from a country they have not visited |
| First seen | When the session was created | A session older than any device they still use |
| Last active | The last request or token refresh | A session still in use after they lost the device |
| ”This device” | The session making the request | Which row not to end by mistake |
MDN names two situations in which a site might want to end a user’s sessions: some high-risk operation such as “attempting to change, or actually changing, the user’s credentials on the site”, or a password reset, and some grounds for thinking a session ID might have been stolen, for example “a sign-in from a new IP address or device”. The checklist covers both: the first through line 4, the second through the list a user can read and the button beside each row.
My working rule is these five lines:
- Every sign-in creates a session record the server can list.
- The account page shows those records with enough detail to spot a stranger.
- The user can end any one of them.
- The user can end all of them, and a password change or reset does it automatically.
- Ending a session stops access within a stated time, and that time is tested.
This is deliverable 1.9 of the Production Hardening Sprint, session management and global sign-out, which provides session listing and a way to sign out across all active sessions.
What goes wrong without it
Four situations bring the feature up, each with the message a founder hears in support:
| Situation | What the user does | What can happen | Checklist line that fixes it |
|---|---|---|---|
| A lost phone still logged into the account | Changes the password from a laptop | Whether the phone’s session ends depends on the provider, and a token already issued can keep working until it expires | Lines 4 and 5 |
| A shared or borrowed computer | Nothing; they never clicked sign out and cannot reach the machine again | The session stays open for whoever sits down next | Lines 2 and 3 |
| A former team member on a shared login, or a contractor’s laptop after the contract ends | Changes the password, or nothing | The old device keeps its session | Lines 3 and 4 |
| A ticket that says “someone is in my account” | Writes to support | Support cannot tell them what is signed in | Lines 1 and 2 |
The first row is the one that reaches the founder as an urgent email. The user has already done the sensible thing, a new password, and the stolen phone can still load their data. Supabase ends other sessions on a password change “depending on configuration”, Clerk when the app passes a flag for it, and Firebase ends refresh tokens on “password or email address updates” but not the ID token already issued. The master table further down puts the four providers side by side.
The feature has limits. Stopping a session from being stolen in the first place is not its job: that falls to cookie flags, transport and token lifetime, the ground of JWT security. Nor does it replace a second factor, and how to implement two-factor authentication is a separate job. What it gives the user is a way out once something has gone wrong.
In the third-party apps I audited, the Authentication pillar averages 52.8 out of 100, scored on 14 of the 21 apps. Those 21 are the apps I audited in June and July 2026: 11 public vibe-coded apps audited exhaustively across all 12 pillars, and 10 held-out apps audited blind. They are a selected set of audited apps, not a random sample, so the average is no rate for AI-built apps in general, and it is not a count of apps missing a session list.
How to do it on Supabase, Clerk, Auth.js and Firebase
Supabase Auth, Clerk, Auth.js and Firebase Authentication are, in my reading, the auth layers AI builders scaffold most often. A custom auth system follows the same three steps below with its own sessions table. Every cell in this table comes from the provider’s documentation, read on 3 October 2026, not from a test run for this page.
| Provider | Where sessions are stored | How to list them | Call that ends all | Password change and other sessions | Tokens already issued | Source and date |
|---|---|---|---|---|---|---|
| Supabase Auth | The auth.sessions table | Not stated in Supabase’s docs | signOut(), whose default scope is global | Sessions end “depending on configuration” | The access token “will still be valid until it expires”; default expiry 1 hour | Supabase docs, 2026-10-03 |
| Clerk | Clerk’s Backend API (GET /sessions) | getSessionList({ userId }) | revokeSession() per session; the calls that revoke all at once also ban the user or mark the password compromised | Ends them when updatePassword() is called with signOutOfOtherSessions | The session token “expires after 60 seconds” | Clerk docs, 2026-10-03 |
| Auth.js, database strategy | A Session row in your database | Not stated in Auth.js’s docs; one user can have several Session rows | Delete the user’s Session rows | Not stated in Auth.js’s docs | No signed token; sign-out deletes the row | Auth.js docs, 2026-10-03 |
| Auth.js, JWT strategy | Only in the cookie | None | Not possible before expiry without a blocklist | Not stated in Auth.js’s docs | ”valid (the server will accept it) until it expires” | Auth.js docs, 2026-10-03 |
| Firebase Authentication | Not stated in Firebase’s docs | Not stated in Firebase’s docs | revokeRefreshTokens(uid) | Refresh tokens end on “password or email address updates” | ID tokens “last for an hour” unless checked with checkRevoked | Firebase docs, 2026-10-03 |
List active devices for a user: the session record and what to show
Listing active devices for a user needs a record per sign-in: a session id, created and last-active times, the user agent and the IP address. The account page turns those into a browser, an operating system and a rough location, and marks the current device. That record is my working rule, not a provider default.
| Field | Where it comes from | Why the user needs it |
|---|---|---|
| Session id | The provider’s session id, or one the app creates at sign-in | The handle the revoke button acts on |
| User id | The signed-in user | Keeps the list to the caller’s own sessions |
| Created at | The sign-in time | Shows how old the session is |
| Last active at | Updated on a request or a token refresh | Shows whether it is still in use |
| User agent | The request header at sign-in | Becomes “Safari on iPhone” or “Chrome on Windows” |
| IP address | The request at sign-in | Becomes a city or region, never a precise place |
Where that record already exists depends on the provider. Supabase stores each session in the auth.sessions table, as Supabase’s sessions guide puts it, and every access token carries a session_id claim that matches the table’s primary key. A call that lists one user’s sessions is not stated in Supabase’s docs, so in my reading the list is a server-side read of that table. Clerk’s getSessionList() “Gets a list of sessions for either the specified client or user”, and Clerk’s page for its <UserProfile /> component says “Active devices and sessions can be viewed and managed in one place”.
Auth.js keeps rows only with the database strategy; per Auth.js session strategies, JWT is the default “unless a database provider is configured”, and the Credentials provider (email and password) “can only be used if JSON Web Tokens are enabled for sessions”. An Auth.js app that signs users in that way has no session rows and writes its own record. Firebase’s session docs describe ID tokens and refresh tokens, with no listable session store stated, so a Firebase app also writes its own record at sign-in.
Mark the row whose id matches the current request as “this device”. The list endpoint returns only the caller’s own sessions and is a protected route like any other; an admin view of someone else’s sessions belongs behind the checks in the admin panel security checklist. I treat the IP address and user agent as personal data and give them a retention window, so old rows are deleted rather than kept forever.
How to add sign out everywhere, and sign out one device
Sign out everywhere is a server action that ends every session for the signed-in user, plus the same action fired on a password change or reset. Supabase, Clerk, Auth.js with database sessions and Firebase each document a way to do it. My working rule: ask for recent re-authentication before it runs.
I’d build it in six steps, my working rule rather than any provider’s:
- 01 Add a server action that signs the current user out of every session, using the call in the provider table above.
- 02 Add a revoke for a single session, behind the button beside each row of the device list.
- 03 Ask for the password or a second factor again before either action when the session is old, so someone holding a stolen device cannot sign the owner out.
- 04 Run the same revoke-all on password change, password reset, email change and second-factor reset, unless the provider already does it.
- 05 Email the user that their sessions were ended, and from which device.
- 06 Write an audit entry with who did it, when, and from which session.
On Supabase, signOut() takes three scopes. Supabase’s signOut reference warns that “the default scope is ‘global’”, which signs the user out of every device, while local ends only the current session and others ends every session but the current one. So a plain Sign out button that passes nothing already signs the user out everywhere; the same reference says { scope: 'local' } is “usually what apps want” on a Sign out button, which leaves sign out everywhere as a button of its own.
// Sign out of every device (global is the default scope)
const { error } = await supabase.auth.signOut()
// Sign out of all other sessions, keep the current one
await supabase.auth.signOut({ scope: 'others' })
Clerk’s docs give no plain sign-out-everywhere call: banUser() revokes all of a user’s sessions but also bans the user, and setPasswordCompromised() does so only with revokeAllSessions set and prompts a password reset at next sign-in. So the server lists the sessions with getSessionList() and revokes each one; Clerk’s revokeSession reference describes revokeSession() as “Revokes the given session”, which also serves the single-device button. On a password change, Clerk’s updatePassword() takes a signOutOfOtherSessions option, “Whether to sign out the user from all their active sessions once their password is updated”, so the app has to pass it. With Auth.js’s database strategy, the strategies page lists “sign out everywhere” among what database sessions make possible: delete the user’s session rows for all devices, or one row for one device.
Firebase revokes a user’s refresh tokens as a whole with revokeRefreshTokens(uid), and its docs suggest doing so “when a user reports a lost or stolen device”; password and email updates end refresh tokens, and password resets revoke them automatically. Supabase’s three scopes are global, local and others, and none of them names one chosen device other than the current one. On those two providers, my working rule is that the single-device button deletes the app’s own record for that session, and the server refuses any request whose record is gone.
The audit entry in step 6 follows audit logging best practices. Deleting an account ends its sessions too, part of an account deletion feature.
Why the old token can outlive the sign-out, and how to close the gap
Revoking a session stops new access tokens, and where the app accepts signed tokens it does not cancel one already issued, so a signed-out device can keep working until that token expires. Closing the gap, by my working rule, means shortening the token lifetime or checking the session store on payment, admin and export routes.
In my reading of the mechanism, an access token is a signed statement: the server checks the signature and the expiry and accepts it without asking anyone. The providers say the same in their own words. Supabase revokes the refresh token on sign-out and leaves the access token to run out, as the provider table quotes, and its access tokens last “usually between 5 minutes and 1 hour”. Clerk’s session token “expires after 60 seconds”. On Firebase, “Firebase ID tokens are short lived and last for an hour”, and a server learns that one was revoked only if it passes the checkRevoked flag to verifyIdToken. The exception is Auth.js’s database strategy: on sign-out “the session is deleted from the database”, so no signed token is left to outlive it.
Take an app built on Firebase Authentication. A customer whose phone was stolen changes the password from a laptop, which ends the refresh token the phone holds. The ID token already on the phone lasts for up to an hour, and the app’s server verifies ID tokens without the checkRevoked flag, so the phone’s requests can keep working until that token runs out. The lesson I take from it: a password change stops new tokens, and a server that checks revocation on its sensitive routes is what stops the one already issued.
| Approach | How fast access stops | Cost |
|---|---|---|
| Shorten the access token lifetime | At the new expiry. Supabase sets it in the Auth settings and advises against going below 5 minutes; Clerk’s is already 60 seconds; a Firebase setting to shorten it is not stated in Firebase’s docs | More refresh traffic |
| Check the session or revocation state on sensitive routes only | At once on payments, admin, data export, and password and email change; at expiry elsewhere | One store read on each of those calls |
| Check on every request | At once everywhere | A store read on every call |
For a small SaaS I’d pick the middle row. Supabase gives the same advice for its case: check that the token’s session_id claim still has a row in auth.sessions, and keep that check “only for the most sensitive actions”. On Firebase the route check is verifyIdToken with checkRevoked, which Firebase’s guide to managing user sessions calls “an expensive operation, requiring an extra network round trip”, a cost that only those routes pay. The design of expiry, rotation and revocation lists belongs to JWT security.
How to verify it
Active session management is verified with three sessions for one test user: revoke one, then all, and call a protected route with each old token. By my working rule, the test passes when revoking all sessions blocks access: every call refused within the window the app states, and a password change ending the rest unprompted.
The test I’d run has seven steps, on staging with test accounts, and it is my working rule:
- 01 Sign in as one test user in three places: browser A, browser B in a private window, and a phone.
- 02 Open the device list in A and confirm three entries, with A marked as this device. Keep: a screenshot of your own staging list.
- 03 In A, revoke B only. In B, reload a protected page and call a protected API route directly with the old token from B. Keep: the seconds until each is refused, with timestamps and status codes.
- 04 In A, sign out everywhere, then repeat the protected request on the phone. Keep: the refused request from the phone.
- 05 Confirm A itself is signed out or kept, whichever your design says.
- 06 Sign in again on B and the phone, change the password in a fresh session, and confirm the others are refused within the stated window without pressing anything. Keep: the refused requests and their timestamps.
- 07 Where the app holds a refresh token, try the old one and confirm it cannot get a new access token. Keep: the error.
The pass condition is access stopping within the window the app states: the access token lifetime on ordinary routes, or at once on the sensitive ones. Keep the audit log entries beside the timestamps. Step 7 applies on Supabase and Firebase, which both issue refresh tokens; Auth.js’s database strategy keeps a session ID in a cookie instead, so there it has nothing to try.
In the Production Hardening Sprint, we verify deliverable 1.9, session management and global sign-out, this way: create multiple sessions, revoke them, and confirm protected access stops. Deliverable 1.2, session and token controls, is verified this way: confirm expired and revoked sessions cannot continue accessing protected resources.
Where the sprint does this
Deliverables 1.9 and 1.2 are the two verified above. Deliverable 1.10, the sensitive-action audit log, logs role changes, deletions, and billing changes with the actor, action, and time, and protects access to the log; it is verified this way: trigger the listed actions and verify accurate, access-controlled audit entries. The session list and the sign-out button come in as supporting interfaces: the work includes only the interfaces the listed controls need, such as session management, account deletion and billing self-service, and new features that change the product’s core capabilities are separate work. Deliverable 13.1, the production readiness report, delivers the result for every scope item, the work completed, and its verification evidence; it is verified by accounting for all 123 IDs, keeping failures visible until resolved and explaining genuine non-applicable items. Each of these is listed in the published scope.
Common questions about active sessions and signing out everywhere
What does active session mean?
An active session is a sign-in the server still accepts: the device holds a session id or an unexpired token, so it acts as the user without signing in again. My working rule: it stays active until it expires, the user signs out, or the server ends it, and the device list above shows one row for each.
How do you know if your session is hijacked?
The clearest sign, in my reading, is a device or location in your account’s session list that you do not recognize, or a sign-in alert or settings change you did not make. That makes the device list the detector: a user can spot a stranger’s session only if the app shows one. On the server side, MDN gives a sign-in from a new IP address or device as an example of some grounds for suspecting that a session ID might have been stolen, and says a site might then want to “invalidate a user’s sessions and require reauthentication”.
What are the best practices for session management?
Four practices matter most, in my reading: expire idle sessions, renew the session ID after a privilege change, give users a logout that ends the session on the server, and ask for reauthentication after high-risk events. OWASP’s Session Management Cheat Sheet says “All sessions should implement an idle or inactivity timeout”, that the session ID “must be renewed or regenerated by the web application after any privilege level change”, that logout means the application “must invalidate the session at least on server side”, and it lists “Attempted or completed password changes” and a “Login from a new or suspicious IP address or device” as reasons to reauthenticate. NIST SP 800-63B, Sec. 2.2.3, sets at AAL2 an overall reauthentication timeout that “SHOULD be no more than 24 hours” and an inactivity timeout that “SHOULD be no more than 1 hour”. Token lifetimes and rotation sit with JWT security.
How do I see all devices connected to my account?
On a Google account, go to Security & sign-in and select Manage all devices on the Your devices panel; Google lists the devices where you are signed in now or were in the last few weeks, and you can sign out of any of them there. On a Microsoft account, Microsoft keeps a separate page for managing the devices used with the account, and Sign out everywhere sits under Advanced security options on the account’s security dashboard. In any other app, you can see them only if the app has a session list like the one this article describes.
Can I sign out of my Microsoft account everywhere?
Yes: sign in to Advanced security options on the Microsoft account security dashboard, scroll to Sign out everywhere and select Sign out. Microsoft says you will be signed out of browsers, apps and anywhere else the account is used within 24 hours, except an Xbox console, and that “Signing out may take up to 24 hours.” In my reading that delay is the same gap between ending a session and the access already handed out, at consumer scale.
The checks in this guide show you where the app is open. The sprint below closes those gaps, tests the result and writes the evidence down.
Built it with AI. Now it has to hold up for real customers.
The Production Hardening Sprint takes the app you already have and builds the production foundation underneath it. Authentication and access rules, payments that stay consistent, error handling, monitoring, backups, automated tests and a documented handover. Our engineers work inside your existing codebase for ten working days. All 123 deliverables are included, and you get the evidence for each one.
See the Production Hardening Sprint →
$2,500 fixed price · 10 working days · One codebase