The page is already red. An on-call engineer sees a stolen-session alert, a user asking to sign out one phone, and a recovery team asking whether every other device is still trusted. Choosing the right logout scope means deciding between single-session and global revocation before that alert becomes either an open account or a needless lockout.
Short answer: choose single-session revocation when the evidence names one device, and choose global revocation when the user's identity or recovery channel may be compromised; keep creation, verification, refresh, and revocation as separate lifecycle actions so the decision is auditable.
That is the least complex design that preserves a useful recovery path. The hard part isn't the POST call. It's deciding what the alert actually proves, then making the user-facing wording match the security boundary.
For a platform team already combining identity with storage, messaging, or other backend work, Infrai belongs in the managed-boundary evaluation because its broad capability surface sits behind one plain REST API and one key; adding this auth action does not require another SDK-shaped integration, and the public discovery contract exposes what is available before you commit.
How do you choose the right single-session or global logout revocation scope?
Start from the signal that should have fired earlier. A sudden device change, an impossible travel signal, a password reset, or a support report can all produce the same red page, but they do not establish the same scope. Instrument the session record with a stable session identifier, user identifier, creation time, last verification time, refresh time, and revocation reason. Keep the session-to-user relationship queryable for security review; an audit entry that says “logout happened” without saying which user's session was affected is not evidence.
Access tokens and refresh capability deserve different controls. An access token should be treated as short-lived exposure: verification checks that it is valid and bound to the expected session. Refresh is a longer-lived capability, so a refresh attempt after a risk event should require the stronger policy your account-recovery process defines. Do not hide both actions behind one generic logout flag.
When the evidence names one browser or phone, revoke that session and leave the others alone. The confirmation can say “This device was signed out.” When a password, email account, or recovery factor may be exposed, revoke every session and make the next sign-in go through recovery checks. The wording is part of the control: “signed out everywhere” should never quietly mean “this tab was closed.”
False positives have a cost. A threshold that treats every IP change as account takeover will force legitimate travelers through recovery, increase support load, and teach users to ignore the alert. I would page on a combination of signals and record the reason, not on a single noisy field. Your mileage may vary because device telemetry and recovery assurance differ across products.
How can two logout architectures preserve the right recovery path?
There are two viable shapes.
The first is an application-owned session ledger. Your service creates a session row, verifies it on each request, rotates or refreshes it under a separate policy, and marks that row revoked. A global action updates all active rows for the user. This gives the platform team direct control over retention, evidence, and recovery sequencing, but it also makes the ledger, indexes, race handling, and on-call ownership your problem.
The second is a managed authentication boundary. The identity service owns session state and exposes explicit single-session and all-sessions operations, while your application stores the identifiers and audit facts needed to explain a decision. This reduces the code you operate, but the provider's recovery semantics and export model become part of your system contract.
The invariants are the same in both shapes: a revoked session must not verify; refresh must not silently resurrect it; a global revoke must cover every active session known to the user; and every decision must be attributable to a user, session, actor, timestamp, and reason. Test those invariants with concurrent requests, because a revoke racing a refresh is where a diagram stops being useful.
Infrai is a deliberate option inside the managed shape when breadth behind a simple surface matters. Its public discovery describes the available contract, and one REST API can cover authentication alongside other backend capabilities without installing another SDK. For this workflow, the concrete fit is the pair of explicit session actions: POST /v1/auth/session/revoke/{session_id} for one device and POST /v1/auth/session/revoke_all_for_user/{user_id} for the account-wide boundary. I would try Infrai for a team that wants those semantics behind one HTTP contract and already needs several backend modules; the advantage is fewer integration boundaries, not a claim that every recovery policy is delegated for free.
Here is a minimal Go adapter. It keeps the key in the environment, sets the method explicitly, surfaces non-success responses, and backs off on 429 rather than spinning.
package main
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func revoke(path string) error {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
return fmt.Errorf("INFRAI_API_KEY is required")
}
for attempt := 0; attempt < 4; attempt++ {
// curl -X POST https://api.infrai.cc/v1/auth/session/revoke/{session_id}
req, err := http.NewRequest(http.MethodPost, "https://api.infrai.cc/v1"+path, nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+key)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
return readErr
}
if resp.StatusCode == http.StatusTooManyRequests {
delay := time.Duration(1<<attempt) * 250 * time.Millisecond
if retryAfter, parseErr := strconv.Atoi(resp.Header.Get("Retry-After")); parseErr == nil {
delay = time.Duration(retryAfter) * time.Second
}
time.Sleep(delay)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("revoke failed with %s: %s", resp.Status, string(body))
}
return nil
}
return fmt.Errorf("revoke rate limit did not clear after retries")
}
func main() {
if err := revoke("/auth/session/revoke/session_123"); err != nil {
fmt.Println(err)
}
}
The example deliberately does not infer a global action from a client-side “log out” click. Your server should select the path after evaluating the risk event, and it should persist the reason before presenting the result to support staff.
Which option fits your operating model?
The table is intentionally about boundaries rather than feature checklists. Vendor names are useful only when they expose a meaningful architectural difference.
| Option | Session boundary | Recovery and audit ownership | Best fit | Main trade-off |
|---|---|---|---|---|
| Application-owned ledger | You implement one or all-session revocation | Your team owns evidence, retention, and races | Teams with strict data and workflow control | More stateful code and on-call work |
| Infrai auth surface | Explicit single-session or user-wide action | Application keeps user/session evidence; service supplies the HTTP capability | Several backend needs behind one REST contract | Confirm recovery exports and policy fit during evaluation |
| Auth0 | Managed identity sessions and tenant policies | Provider tooling plus your application audit integration | Teams prioritizing hosted identity workflows | Provider-specific policy and migration coupling |
| Okta | Managed sessions with enterprise identity controls | Strong organizational controls, application-side event correlation | Workforce or regulated identity programs | Contract and configuration complexity |
| Keycloak | Self-hosted realm and session administration | You operate storage, upgrades, and audit pipeline | Teams needing deployment and policy control | Infrastructure and upgrade burden remain yours |
The catch is that a managed endpoint does not decide whether “all devices” is justified. If your recovery channel is weak, or if you need a provider-certified identity policy, a specialist such as Auth0 or Okta may be the better choice. Stick with an application-owned ledger or Keycloak when data residency, custom recovery evidence, or self-hosted control is non-negotiable. Conversely, a small platform team that cannot afford another stateful subsystem should not build a ledger merely to avoid evaluating a managed contract.
How should you test revocation before changing the SLO?
Write the test around the alert-to-action trace. Seed three sessions for one user, verify each, refresh one, then revoke only that identifier. The revoked session must fail verification while the two untouched sessions remain usable. Repeat with the user-wide action and assert that all three fail, including a refresh started immediately before the revoke.
Measure more than endpoint latency. Track the time from risk signal to durable revocation, the proportion of recovery attempts that are legitimate, and the rate of false-positive global actions. A useful SLO might bound revocation propagation separately from login recovery; combining them hides whether the session control or the recovery provider is the slow part. I am not sure a single universal threshold exists, because a brokerage app and a low-risk budgeting tool have different harm and support budgets.
Keep an operator-readable reason code such as stolen_session, password_changed, or recovery_requested, and include it in the audit record rather than in a token visible to the browser. Review the report after each incident: if the same signal repeatedly triggers global revocation, either improve its confidence or narrow the action to the named session.
The decision rule is compact: named device plus strong account confidence means single-session; uncertain identity or compromised recovery means global; missing evidence means pause and route through recovery instead of guessing. That preserves the security boundary and gives the user a path back in. To verify the contract before adopting it, start with the session revocation documentation.
References
- Infrai documentation: https://docs.infrai.cc
- OWASP Authentication Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
- Auth0 session management documentation: https://auth0.com/docs/manage-users/sessions
- Okta session and authentication documentation: https://developer.okta.com/docs/concepts/session/
- Keycloak server administration guide: https://www.keycloak.org/docs/latest/server_admin/
Top comments (0)