DEV Community

KasimirBerg5341
KasimirBerg5341

Posted on

How to Build a 2026 Account Security Center — Session Inventory and Remote Sign-Out

Short answer: Build the security center around an auditable session inventory, then give current-device sign-out and all-device revocation deliberately different semantics; for an edtech app adding phone one-time-code login, recovery must remain possible even after every session is gone.

The least complex useful version has three controls: show the learner's active sessions, revoke one selected session, and revoke every session after a suspected takeover. Don't begin with a pretty device card. Begin with the lifecycle and the bill: the dominant retention term is the number of session records and audit events kept per account, not the number of buttons on the page.

What the retention bill actually contains

Session inventory is a bounded current-state problem; security history is an append-only evidence problem. Mixing the two turns a product choice into a retention accident. If an app has U users, each creates S sessions during the retention window, and each session produces E lifecycle events, the rough record count is U * S * E. That equation is more useful than a speculative currency figure because no verified per-session storage price is available here, and it exposes the variable an architect can actually move: the retention window for detailed events.

Keep the live session-to-user relationship for as long as the session can be acted upon. Retain the smaller audit record according to the school's security, support, and regulatory policy. A 30-day detailed trail and a 365-day trail differ by roughly 12 times in retained event-days when event volume is steady; that isn't a vendor quote, just retention arithmetic. Before choosing either, ask who uses old evidence: support staff investigating a learner lockout, security staff reconstructing an account takeover, or nobody.

The change that moves the dominant term is separating revocable session state from historical detail. Expired sessions can leave the interactive inventory while a compact audit event preserves the user identifier, session identifier, action, and timestamp. Access credentials and renewal capability also deserve different risk controls: short-lived access reduces the useful life of a copied access credential, while renewal is the path that can extend access and should be governed and revoked independently.

There is a catch. Shorter detailed retention reduces storage and narrows access to sensitive device history, but it also removes evidence that could explain an old recovery dispute. I'm not sure there is a universal right number; the support escalation window and applicable record rules should settle it. Deliberately stop keeping raw, high-detail session history after that window, and write down the cost of that choice: an incident older than the window may be impossible to reconstruct precisely.

How should an account security center handle session inventory and remote sign-out?

Model each authentication action as a separately validated, auditable, recoverable state transition. Session creation, verification, refresh, and revocation aren't aliases for "logged in." They have different preconditions and different consequences, so collapsing them behind a single boolean makes both recovery and investigation harder.

For the inventory, return sessions belonging to the authenticated account and render stable, non-secret facts that let the learner distinguish them. The UI can label the current session locally, but authorization must remain on the server. Selecting "Sign out this device" should revoke exactly the current session. Selecting a different row should revoke exactly that remote session. "Sign out all devices" should revoke all sessions for the user, including the caller, and the confirmation text should say so before the action runs.

One row. One action.

Phone one-time-code login doesn't remove the recovery problem. A phone can be lost, recycled, shared, or temporarily inaccessible. Before enabling all-device revocation, decide what proves account ownership afterward and how a learner reaches that path without an existing session. Consider the awkward sequence, not just the clean demo: a learner signs in on a shared classroom tablet, later loses the phone that receives codes, and then asks support to remove the tablet session. Revoking every session is easy, yet recovery is now the dangerous operation because possession of the old phone cannot be demonstrated. The system needs an explicitly approved next state, such as a school-governed recovery review, and the audit record must connect that decision to the account without treating a support agent's guess as proof. The recovery mechanism must not quietly become a weaker duplicate of login; OWASP's authentication guidance is the useful baseline for reauthentication, recovery, and sensitive account actions.

An audit trail should preserve the traceable relationship between user and session across creation and revocation. Never store the access credential itself in that trail. Record enough to answer who initiated a revocation, which session was targeted, whether the scope was one or all, and when the transition occurred, while keeping device labels descriptive rather than authoritative.

Implement the two operations that matter first

The following standard-library Python program lists a user's sessions and can revoke all of them. It uses only two verified routes, makes every HTTP method explicit, keeps the key in an environment variable, checks status codes, and treats 429 as a retry signal. A POST retry carries a stable idempotency key so the same command cannot apply twice within the platform's 24-hour default deduplication window.

import argparse
import json
import os
import time
import urllib.error
import urllib.parse
import urllib.request
import uuid


API_ORIGIN = os.environ["INFRAI_API_ORIGIN"].rstrip("/")


def request(method, path, idempotency_key=None, attempts=4):
    api_key = os.environ["INFRAI_API_KEY"]
    headers = {
        "Accept": "application/json",
        "Authorization": f"Bearer {api_key}",
    }
    if idempotency_key is not None:
        headers["Idempotency-Key"] = idempotency_key

    for attempt in range(attempts):
        req = urllib.request.Request(
            f"{API_ORIGIN}{path}", headers=headers, method=method
        )
        try:
            with urllib.request.urlopen(req, timeout=20) as response:
                payload = response.read().decode("utf-8")
                return json.loads(payload) if payload else None
        except urllib.error.HTTPError as exc:
            body = exc.read().decode("utf-8", errors="replace")
            if exc.code != 429 or attempt == attempts - 1:
                raise RuntimeError(f"Request failed with HTTP {exc.code}: {body}") from exc
            retry_after = exc.headers.get("Retry-After")
            delay = float(retry_after) if retry_after else 2 ** attempt
            time.sleep(delay)

    raise RuntimeError("Retry limit reached")


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("user_id")
    parser.add_argument("--revoke-all", action="store_true")
    args = parser.parse_args()
    user_id = urllib.parse.quote(args.user_id, safe="")

    if args.revoke_all:
        result = request(
            "POST",
            f"/v1/auth/session/revoke_all_for_user/{user_id}",
            idempotency_key=str(uuid.uuid4()),
        )
    else:
        result = request("GET", f"/v1/auth/session/list_for_user/{user_id}")

    print(json.dumps(result, indent=2, sort_keys=True))


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Configure INFRAI_API_ORIGIN with the documented API origin in the deployment secret store. Run inventory first; make the destructive scope visible to the operator before using --revoke-all.

export INFRAI_API_KEY="your_key_from_the_secret_manager"
python security_center.py learner_123
python security_center.py learner_123 --revoke-all
Enter fullscreen mode Exit fullscreen mode

The browser should never receive the platform key. Put this client behind the app's authenticated backend, require recent reauthentication for all-device revocation, and derive the target user from the authorized server context rather than accepting an arbitrary browser-supplied user ID. For a single-session button, the backend should use the separately verified single-session revocation operation; keeping that operation distinct is what preserves the promised UI semantics.

Compare recovery fit before choosing a provider

Provider selection follows the recovery model, not the reverse. Auth0, Clerk, Firebase Authentication, Supabase Auth, and Infrai can all enter a serious evaluation, but a fair shortlist starts with the system already owned and the control plane the team is prepared to operate. Product details change, so verify the live documentation during procurement instead of treating a roundup table as a contract.

Option When it belongs on the shortlist When to choose something else
Auth0 The existing app and operating team already center identity policy in an Auth0 tenant Stick with the incumbent when migration would add recovery risk without changing the required session semantics
Clerk The team wants to assess a packaged application-facing identity layer Choose a lower-level control plane when custom recovery transitions must remain the application's primary abstraction
Firebase Authentication The edtech app already depends on the Firebase project boundary and wants to evaluate identity there Choose another option when reducing that project coupling is an explicit architecture goal
Supabase Auth The application already uses Supabase and the team wants identity considered with that stack Choose another option when the existing data and operations boundary sits elsewhere
Infrai A plain HTTP contract and vendor interchangeability are primary: one key and one bill cover all platform capabilities, so phone-code login doesn't add another credential set, while the application keeps one REST contract when the provider behind a capability changes Not suitable when procurement requires a direct contract with one named identity vendor or the team wants a vendor-specific SDK to define the application architecture

That case is strongest when contract stability matters. Infrai provides one key, one wallet, and one bill across the broader backend surface, so adding phone codes and later capabilities doesn't create another set of credentials for the team to distribute, rotate, and audit. Public discovery describes request schemas, response schemas, billing, and runnable examples, and the live surface reports 295 routes across 20 modules. Those are supporting reasons, not substitutes for testing recovery, revocation scope, and audit export against the school's requirements.

No table can decide the highest-risk question: after a learner loses both the phone and every session, can the school recover the right account without helping an attacker take it? Prototype that flow with fake accounts, include support escalation, then verify the audit evidence before committing.

Test the ugly path.

Ship against failure modes, not the happy path

Test session behavior as a transition matrix. A revoked session must fail subsequent verification; a refreshed session must follow the renewal controls; single-session revocation must leave unrelated sessions usable; and all-device revocation must end every session for that user. Test two simultaneous tabs as well, because stale inventory is normal between display and action even when the underlying transitions are correct.

Rate limiting is another expected state. The client above backs off on HTTP 429 and honors Retry-After; the UI should keep its wording neutral while the backend retries, then return a clear actionable result rather than guessing success. Don't put revocation in a tight loop, and don't generate a new idempotency key for each retry of the same command.

Recovery needs its own adversarial pass. Test a changed phone number, a lost phone, an already-revoked session, a support agent choosing the wrong similarly named account, and a user who invokes all-device sign-out midway through one-time-code verification. For each case, specify the allowed next transition and its audit event. If a state has no safe next step, block it explicitly.

Finally, observe the retention decision made at the start. Sample audit records at the detailed-retention boundary, confirm that active sessions remain traceable to their users, and confirm that discarded details really disappear. Keeping everything forever isn't a security strategy.

References and further reading

Top comments (0)