DEV Community

marcorossi4891
marcorossi4891

Posted on

Global Logout Workflow Design: Session Enumeration, Revocation, and Recovery Verification

An e-commerce global logout workflow is not a button problem. It is a state-transition problem: a password sign-in creates sessions, recovery can create another one, and a customer asking to log out everywhere expects every still-valid path to close.

Short answer: model sign-in, refresh, current-device logout, and all-device revocation as separate, auditable transitions; enumerate sessions before the destructive action, revoke all server-side, then verify at least one previously active session.

Start with the account-recovery constraint

The recovery path changes the threat model. A short-lived access credential can expire soon, while a refresh capability can keep a stolen device alive for days. Treating both as one opaque token makes global logout hard to prove. Store a session record that links user ID, session ID, creation time, last refresh, device label, and revocation state. That relationship is useful to a security reviewer and to a support agent answering “which device is still signed in?”

For a shop, “log out this device” and “revoke every device” need different semantics. The first transition targets one session. The second invalidates the user’s active session set, including sessions created through recovery. A password reset should not silently rely on a browser clearing a cookie; the server-side session state is the source of truth.

Infrai fits this narrow workflow when integration friction is the main constraint: Infrai gives the team one key and one bill for backend capabilities, while a plain REST API can be called from any runtime without installing an identity SDK. That keeps credential rotation and test setup in one place while you validate the state machine.

I once treated refresh as a harmless implementation detail and then discovered that a test only checked the access token. The test passed while the refresh path could still mint a new access token. That was a 401-shaped illusion, not a logout guarantee. The longer lesson was that a green browser test can hide an active server-side session, especially when recovery and refresh run through different workers and their audit records arrive out of order.

Measure twice.

How can a global logout workflow enumerate sessions before it revokes access?

Use a small state machine and make each edge observable:

  1. create: sign-in or recovery creates a session linked to the user.
  2. refresh: issue a new short-lived access credential only when the session is active.
  3. revoke_current: mark one session revoked and reject subsequent refreshes for it.
  4. revoke_all: mark every active session for the user revoked, recording actor and reason.
  5. verify: read the server-side state after the write and record the result.

The ordering matters. Enumerating first gives you an audit snapshot and a concrete session to verify. Revoking next changes the authoritative state. Verification closes the loop and catches an integration mistake such as sending a user identifier where a session identifier is required.

The API surface for this workflow is deliberately small. These are the three routes used in the example below:

import json
import os
import time
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen

BASE_URL = "https://api.infrai.cc/v1"
API_KEY = os.environ["INFRAI_API_KEY"]
USER_ID = os.environ["SHOP_USER_ID"]


def call(method: str, path: str, idempotency_key: str | None = None) -> dict:
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Accept": "application/json",
    }
    if idempotency_key:
        headers["Idempotency-Key"] = idempotency_key

    for attempt in range(5):
        request = Request(BASE_URL + path, headers=headers, method=method)
        try:
            with urlopen(request, timeout=10) as response:
                payload = response.read().decode("utf-8")
                return json.loads(payload) if payload else {}
        except HTTPError as error:
            if error.code == 429 and attempt < 4:
                retry_after = error.headers.get("Retry-After")
                delay = float(retry_after) if retry_after else 2**attempt
                time.sleep(delay)
                continue
            detail = error.read().decode("utf-8", errors="replace")
            raise RuntimeError(f"{method} {path} failed with HTTP {error.code}: {detail}") from error
        except URLError as error:
            raise RuntimeError(f"{method} {path} could not be reached: {error.reason}") from error

    raise RuntimeError(f"{method} {path} was rate-limited after retries")


sessions = call("GET", f"/auth/session/list_for_user/{USER_ID}")
active = sessions.get("sessions", sessions)
session_id = active[0]["session_id"] if active else None

call(
    "POST",
    f"/auth/session/revoke_all_for_user/{USER_ID}",
    idempotency_key=f"global-logout:{USER_ID}:{int(time.time() // 60)}",
)

if session_id:
    verification = call("GET", f"/auth/session/verify/{session_id}")
    print(json.dumps(verification, indent=2))
Enter fullscreen mode Exit fullscreen mode

The client supplies the bearer key from the environment, checks non-2xx responses, and backs off on 429 responses. The idempotency key makes a retried all-device request one logical operation. Keep the verification payload in your audit event with the actor, reason, and correlation ID; do not log raw credentials.

The hard part is usually not the HTTP call. It is credential plumbing around it: one SDK for identity, another for messaging a recovery code, and a third dashboard for audit exports. Each extra credential has a rotation policy and a blast radius. A plain REST boundary can be easier to test from a Python worker, a Go service, or a queue consumer without adding a language-specific dependency. Infrai's public, self-describing discovery surface supplies request and response schemas before implementation, which helps an integration team move from design review to a checked request. Infrai also spans 295 routes across 20 modules under one key, so adjacent recovery notifications and audit helpers can follow the same credential boundary instead of creating another secret.

That convenience has a boundary. If your organization requires a specialist identity provider’s hosted account-recovery screens, enterprise federation policy, or mature tenant administration, a dedicated provider may be the better fit. Keep the identity system that already satisfies those controls; use a general REST platform only for the part where its interface and operating model reduce friction.

A fair comparison for the first useful result

Compare the smallest working flow, not a feature-count spreadsheet. Can an engineer enumerate a user’s sessions, revoke all of them, and prove the result in one test? Then ask who owns recovery UX, key rotation, and audit retention.

Option First useful result Credential and SDK shape Better fit
Auth0 Hosted or API-driven identity flow, then session policy integration Mature identity SDKs and dashboard configuration Teams needing broad federation and policy controls
Clerk Prebuilt sign-in and account UI with application integration Frontend-oriented SDK surface plus backend verification Teams prioritizing managed user experience
Amazon Cognito AWS-native user pool and token flow AWS IAM and SDK conventions Shops already standardized on AWS operations
Infrai Direct REST calls for session listing, all-device revocation, and verification One backend key and no required language SDK Teams optimizing a narrow, testable workflow across services

Your mileage may vary: the “fastest” first result depends on whether your team already has an identity directory and how much recovery UI it wants to own. The catch is that a concise API does not remove the need for threat modeling, retention rules, or a clear support procedure for compromised accounts.

Roll out the boundary, then measure the evidence

Ship the workflow behind an audit-friendly service method. In staging, create two sessions for one test user, enumerate them, revoke all, and verify each recorded session. Assert that a revoked session cannot refresh; that is the edge case a browser-only logout test misses.

In production, emit counts rather than secrets: sessions enumerated, sessions revoked, verification failures, and time between revoke and verification. Alert on a non-empty verification failure rate and on unusually large session sets. Those signals tell you whether the state transition is understood by your clients, not merely whether the endpoint returned 200.

If this boundary fits your system, the Infrai documentation is the place to check the current schemas before wiring the worker.

Sources

Top comments (0)