Skip to content
downpipes docs

Session management: terminating and rotating sessions

A console session is the short-lived, signed cookie the engine issues after a successful login, so the browser does not re-run the passkey or identity-provider ceremony on every request. This page is for a self-hoster who needs to respond when something goes wrong with one: a laptop is lost, a phone is stolen, or a team member leaves and their access must end cleanly and now.

There are three ways to terminate sessions, each with a different scope and a different person allowed to run it. The right one depends on whose sessions you need to end and why. This page explains all three actions, the two bounds that limit every session regardless (a twelve-hour absolute cap and a two-hour idle timeout), and how session termination relates to switching off an identity provider.

The engine does not keep a server-side table of live sessions. A session is a server-signed token of the form body-then-MAC, where the MAC is HMAC-SHA-256 over the body keyed with a signing key the engine generates once and keeps inside its Durable Object. Verification happens inside the Durable Object over a key that never leaves it, and the body is never trusted until its MAC verifies, so a forged or tampered token cannot set an email or extend an expiry.

Two properties of this model matter when you are responding to an incident.

The first is the session lifetime, which has two separate bounds. The outer bound is an absolute expiry set once at mint, twelve hours out, and never slid forward past that ceiling, so a leaked cookie is useless by the next working day no matter what. The tighter bound is an idle timeout: a session that sees no activity for two hours is rejected at the verify gate independent of the twelve-hour cap, so a leaked but unused cookie stops working after roughly two hours of inactivity, well before that next working day. An active session is kept alive by a sliding re-mint that runs about every fifteen minutes and refreshes the session’s last-activity marker, but the re-mint preserves the original issue time and the absolute expiry, so it can never push a session past the twelve-hour cap. The cookie is also HttpOnly so script cannot read it, Secure so it travels only over HTTPS, and SameSite=Strict so it is never sent on a cross-site request.

The second is that the role is never carried in the token. The session body holds identity, the verified email and the stable subject the role table keys on, but never the role or the capabilities. The engine re-resolves the role from its own tables on every single request. This is why a demotion or a removal takes effect on the very next request rather than waiting out the cookie: lowering someone’s role immediately changes what their existing session can do.

Re-resolution downgrades, it does not terminate

Re-resolving the role on each request means a demoted member loses the higher capabilities at once. It does not, on its own, end their session. An offboarded member whose role is removed falls back to viewer, which still leaves a read surface live until the cookie expires. That is exactly why offboarding additionally terminates the session, covered below.

The three termination actions

Action Scope Who can run it Fresh passkey needed? Effect on the actor
Sign out my other sessions Every other session for your own email Any member with a first-party session No Your current session is re-minted and stays alive
Sign out a member’s sessions All sessions belonging to one named member An admin holding roles.write Yes None, unless you terminate your own
Sign out everyone Every session for every member Owner only Yes Your other tabs die too; you re-authenticate

The passkey column is step-up re-authentication, and the split in it is deliberate rather than incidental. The two actions that reach a session which is not the caller’s own are gated; the self-scoped one is not. A stale session that survived a suspected compromise could otherwise sign the legitimate operator’s other tabs out and leave them unaware anyone else was acting, which is the exact profile the gate exists for. Signing out your own other sessions bumps only your own epoch, so it stays friction-free for the case you reach for after losing a device. A bare break-glass token session and a Cloudflare Access session are exempt from the gate, as they are everywhere else.

Sign out my other sessions

This is the self-service action for “I am still here, but sign out everywhere else”, which is what you reach for after losing a second device you were signed in on. The engine bumps your own session epoch, which invalidates every session minted for your email before the bump, then re-mints your current session carrying the new epoch and re-issues that cookie. The result is that your other sessions die on their next request while the tab you are working in stays alive. A bare-token break-glass caller has no first-party session to manage, so it is refused.

Sign out a member’s sessions

This is the admin action for ending one named member’s sessions without touching anyone else, for example when a colleague reports a stolen phone. It is gated on roles.write, so an owner or an access-admin can run it. The engine bumps the target email’s session epoch so all of their email-keyed sessions die on the next request, and for a member who has authenticated through an identity provider it also bumps the per-subject revocation axis, so their identity-provider sessions die too and not only the email-keyed ones. An access-admin can terminate any non-owner member’s sessions, but only an owner may terminate an owner’s sessions.

Sign out everyone

This is the account-wide reset, the in-account equivalent of a global sign-out, for when you suspect the signing key or a session has been compromised. It is owner-only.

Sign-out-everyone signs you out too

Sign-out-everyone does not leave the person who runs it signed in. It works by deleting and rotating the session signing key, so every outstanding token fails its next MAC verify, including the owner’s own other tabs. A fresh key is generated on the next login. Your recovery codes are not affected. They used to be, because the codes were verified under the session key the action deletes, so signing everyone out silently destroyed the break-glass an owner would need if the compromise they were responding to were real. The two secrets have opposite lifetimes and are now separate records, and the recovery key is materialised before the session key is deleted, so a printed recovery sheet keeps working across a sign-out-everyone. Do not regenerate your codes after running it: regenerating replaces the working set. Expect to re-authenticate immediately after running it, and expect everyone else to as well. This is the point of the action: it ends every session at once rather than chasing them member by member.

Because it rotates the one signing key for the whole account, signing everyone out is reserved to an owner. The engine re-resolves the caller’s role and requires owner, on top of the roles.write gate, so an access-admin cannot sign the whole account out.

Logout and offboarding fail a session closed on the next request

Two everyday events also end sessions, and both work by bumping the session epoch so the affected tokens fail closed at the verify gate, rather than relying on a client-side cookie clear.

An explicit logout is a real server-side termination, not merely a cookie clear in the browser. The engine resolves the identity from the presented token, then bumps the per-email epoch and the per-subject not-before instant. The just-logged-out token, and any copy of it that was exfiltrated, then fails closed on its next request. The browser also drops the cookie, but the server-side bump is what makes the logout meaningful against a stolen copy.

Offboarding a member, removing their role entirely, terminates their sessions for the same reason. Removing the role only downgrades the member to viewer on the next request, which would leave a read surface live for up to the 12-hour ceiling. So offboarding also bumps the per-email epoch and, for a bound member, the per-subject not-before instant, so the next request from any of their sessions fails closed at the verify-time gate.

Ending a session is not the same as ending a way in, and this page only claims the first. A role removal terminates what the person holds now; it leaves the passkey credential they enrolled, any recovery codes they banked and any unredeemed invite in their name untouched, so they can authenticate again and open a session the epoch bump has no quarrel with. Revoking those is a separate act with its own guards, on the Roles and access tab under Sign-in factors, and on the admin API as POST /admin/signin-factors/revoke. Offboard a member is where it and the reasoning for keeping it separate live.

Why this matters (ASVS V7.4.2)

This behaviour is the engine’s answer to ASVS 5.0 V7.4.2: a logout or an offboarding must terminate the associated sessions, so a removed or exfiltrated session does not stay valid for the rest of the 12-hour lifetime. The epoch bump is what makes a removed person’s session, or a copied cookie, fail on the next request rather than living on until it expires. downpipes self-assesses against OWASP ASVS 5.0 at the Level 2 / Level 3-equivalent bar for its authentication component; no product makes you compliant on its own.

How this composes with switching off an identity provider

Session termination and identity-provider revocation are independent mechanisms that compose. The session epoch axes above govern sessions by email and by subject. There is a separate axis keyed on the identity-provider connection, the idpEpoch. Disabling or deleting an identity-provider connection stamps that connection’s epoch, which independently kills every live session that was minted through that connection on its next request, and the verifier additionally re-checks at verify time that the connection still exists and is still enabled.

This means you have two clean ways to cut access for someone signing in through SSO. You can terminate their sessions by member, which bumps their email and subject axes, or you can disable or delete the whole connection, which bumps the connection axis for everyone who signs in through it. The two are described from the SSO side in test, troubleshoot and revoke SSO.

There is one more property worth knowing for an identity-provider session. The member’s groups are snapshotted per subject at sign-in and re-read on every request, and are never carried in the session cookie. So a group change, or a deprovision at the identity provider, takes effect on the next request without waiting out the 12-hour cookie, exactly as a role change does. The cookie carries identity, never groups and never the role.

Where the actions live

The console surfaces all three actions on its access and security screen, calling the engine routes below. The engine is always the enforcement point; the console only mirrors its rules so a button is never a dead end.

Action Engine route Console
Sign out my other sessions POST /sessions/terminate-others A self-service control; the new cookie is re-issued in the response
Sign out a member’s sessions POST /sessions/terminate-user A per-member control, shown to a roles.write holder; step-up gated
Sign out everyone POST /sessions/terminate-all An owner-only control; step-up gated

Where this fits

  • Test, troubleshoot and revoke SSO covers disabling or deleting an identity-provider connection, the independent idpEpoch axis that kills that connection’s sessions.
  • Offboard a member is the end-to-end departure runbook, of which the session termination here is one step.
  • Roles and capabilities defines roles.write and confirms that the role is re-resolved per request rather than baked into a session.
  • Account compromise eviction is the broader incident response, where terminate-all is one lever among several.
The config-version signing key coupling

The session signing key the engine rotates on a terminate-all is the same in-Durable-Object key that signs the config version history. A useful consequence to be aware of: a terminate-all rotates that key, which also invalidates verification of any config-version digest recorded before the rotation, because those versions were signed with the old key. After a terminate-all, treat the verify verdict for config versions captured before that point as reset rather than as evidence that anything was tampered. This is an accurate side effect of one shared key, not a flaw. The change control and config-version-history pages describe the config side of the same key.

Last updated .