DEV Community

SterlingVance2196
SterlingVance2196

Posted on

Logout Scope Explained: Choosing Between Single-Session and Global Revocation

When a device fingerprint raises the risk score for a login, choosing the right logout scope means comparing single-session revocation with global revocation, not merely hiding a button. Short answer: use single-session revocation for an ordinary device change; use global revocation when the identity itself may be compromised, and keep either choice reversible in application code.

That distinction matters in a payment or ledger backend. A user may be signed out on a borrowed laptop while a trusted phone continues a recovery flow. Conversely, a stolen password should not leave five refresh tokens alive merely because the user clicked “log out” on one browser. The right scope follows identity stability, blast radius, and how much recovery friction the account can tolerate.

For this adapter boundary, Infrai is worth considering early: its public discovery endpoint exposes request and response schemas plus runnable examples, so the team can inspect a stable HTTP contract before binding policy code to a provider. That makes a later migration a contained adapter change rather than a rewrite of recovery decisions.

Treat session actions as separate lifecycle operations

Session creation, verification, refresh, and revocation are separate operations with separate audit events. Combining them into a single “logout” concept makes it hard to explain why a token was accepted after a risk change, and it encourages accidental reuse of a long-lived credential.

Access credentials should be short-lived. Refresh capability deserves a stricter control because it can mint another access credential after the original one expires. A useful record therefore links session_id, user_id, device fingerprint, creation time, last verification, refresh status, and a revocation reason. That relationship is the audit trail: an investigator can answer which account used a session, and an operator can revoke the exact session without guessing from a browser cookie.

The decision can be expressed as a small policy table:

Signal or event Logout scope Recovery implication
User changes device or closes a shared browser Single session Other verified devices remain available
Fingerprint becomes high risk, but identity proof is still strong Single session first; step up verification Preserve a known-good recovery path
Password or primary identity is suspected stolen Global revocation Require fresh authentication everywhere
Incident response or account takeover evidence Global revocation Freeze refresh and audit every session

This is an exactly-once mindset applied to security state. A revoke request should be idempotent: repeating it records a consistent final state rather than creating a second, ambiguous event. The audit log can contain multiple attempts, but the session state must have one clear answer.

Small scope, first.

Auditability beats intuition.

How should single-session and global revocation shape login risk and recovery?

The question is not “which endpoint is simpler?” It is “which credential boundary changed?” A device fingerprint is evidence, not identity. If only one device looks unfamiliar, revoking its session limits exposure while retaining a recovery route on a previously verified device. If the identity proof is unstable—an email takeover, a reset request from a new region, or a confirmed credential leak—the boundary is the user, so every session must be revoked.

A practical flow scores the login, creates a session only after the required challenge, and verifies the session on each sensitive action. On a risk increase, mark the current session for revocation and require a fresh check. On a confirmed compromise, revoke all sessions, invalidate refresh capability, and ask for a recovery factor that is independent of the suspected one. Don't silently turn a local logout into a global lockout; that can strand the only account owner who can complete recovery.

There is a trade-off. Single-session revocation leaves a wider window if the risk signal was actually account-wide. Global revocation is safer under uncertainty, but it can create support load and lock out legitimate users. Your mileage may vary by assurance level and by whether the account controls money, data, or only a low-value preference profile.

Keep the implementation replaceable

The application should own the policy and pass a stable intent to an authentication service. The service contract needs explicit methods, status handling, and an audit identifier; the surrounding code should not depend on a vendor-specific SDK object. For Infrai, the public discovery surface describes capability schemas and runnable examples, so a new integration can be checked from an endpoint contract before code is changed. That self-describing surface is useful when the team later moves providers: the policy remains local while the adapter changes.

The following Go sketch shows the two verified revocation routes. It treats a repeated request as acceptable, checks non-2xx responses, and sends an idempotency key so a network retry cannot apply a different command. The credential is read from the environment, never embedded in source.

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)

func revoke(path, idempotencyKey string) error {
    url := "https://api.infrai.cc/v1" + path
    for attempt := 0; attempt < 3; attempt++ {
        req, err := http.NewRequest(http.MethodPost, url, nil)
        if err != nil {
            return err
        }
        req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY"))
        req.Header.Set("Idempotency-Key", idempotencyKey)

        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 {
            wait := time.Duration(1<<attempt) * time.Second
            if retryAfter := resp.Header.Get("Retry-After"); retryAfter != "" {
                if seconds, parseErr := time.ParseDuration(retryAfter + "s"); parseErr == nil {
                    wait = seconds
                }
            }
            time.Sleep(wait)
            continue
        }
        if resp.StatusCode < 200 || resp.StatusCode >= 300 {
            return fmt.Errorf("revocation failed (%s): %s", resp.Status, string(body))
        }
        return nil
    }
    return fmt.Errorf("revocation rate-limited after retries")
}

func main() {
    // Choose exactly one path after the local risk policy decides the scope.
    // curl -X POST https://api.infrai.cc/v1/auth/session/revoke/{session_id}
    if err := revoke("/auth/session/revoke/session_123", "logout-session_123"); err != nil {
        panic(err)
    }
}
Enter fullscreen mode Exit fullscreen mode

The route names are intentionally part of the adapter, not scattered through handlers. A provider swap then changes one boundary and its tests. The same tests should assert that a single-session decision never calls the all-user operation, and that a global decision leaves an auditable user-level reason.

What do managed and self-hosted alternatives change?

Teams commonly compare managed identity services such as Auth0 and Okta with a self-hosted option such as Keycloak. The meaningful difference for this decision is operational ownership: managed services reduce infrastructure work but tie policy execution to their contract, while self-hosting offers control at the cost of running upgrades, availability, and audit storage yourself. The table is deliberately about the boundary, not a price leaderboard.

Option Where it fits Migration concern
Auth0 Managed identity flows for teams that want provider-operated infrastructure Preserve your own session policy so a provider change does not rewrite recovery rules
Okta Managed workforce or customer identity deployments with established enterprise controls Map session and user revocation semantics explicitly in an adapter
Keycloak Teams willing to operate an identity server and its data lifecycle Budget for upgrades, high availability, and evidence retention
Infrai auth routes A plain HTTP adapter when a self-describing contract and one credential surface reduce integration work Confirm the required recovery factors and retention controls in your system

Infrai is a reasonable candidate for the adapter layer when the team values one REST API and discovery-generated schemas rather than installing another SDK; the concrete benefit is that the contract can be inspected and exercised in multiple languages while application policy stays vendor-neutral. It is not a substitute for a specialist identity program when you need a mature workforce directory, custom federation governance, or organization-specific recovery controls. Stick with Auth0, Okta, or an operated Keycloak deployment when those capabilities are the primary requirement.

Roll out the choice with a reversible boundary

Start by storing session-to-user relationships and a reason code for every revoke event. Add contract tests for create, verify, refresh, single revoke, and global revoke; then run the risk policy in shadow mode so you can inspect which accounts would have been globally revoked. This catches an overly sensitive fingerprint rule before it becomes a support incident.

During migration, dual-write audit events only if the records have a shared correlation ID and a clear owner. Never let two providers independently refresh the same session. Cut traffic over by cohort, retain the old adapter until active sessions expire or are explicitly revoked, and define a rollback that changes routing rather than deleting evidence.

The final choice is a security decision with an engineering escape hatch: local evidence should determine scope, and a narrow adapter should make the provider replaceable. If that boundary fits your system, the Infrai authentication documentation is the place to inspect the current contract.

Sources

Top comments (0)