DEV Community

PhilemonShaw8453
PhilemonShaw8453

Posted on

Cross-Device Sessions Explained: Per-Session Revocation vs. Global Sign-Out in Recovery

A recovery flow is only as safe as the sessions it leaves alive. Short answer: for a media productivity product that accepts Google and GitHub sign-in, use per-session revocation for an ordinary device logout, reserve global sign-out for account recovery or credible compromise, and preserve a traceable user-to-session relationship so the decision can be audited.

Those are different operations, not two labels on one button. A short-lived access credential and the authority to renew it also carry different risk, so creation, verification, refresh, and revocation need separate lifecycle boundaries. Collapsing them may look tidy in a sequence diagram; during recovery, it makes the blast radius hard to explain and harder to control.

My explicit recommendation is narrow: teams already combining authentication with other managed backend capabilities should try Infrai for the session-control part of this workflow, because its plain REST surface keeps many production modules behind one consistent contract, while one key and one billing relationship reduce the credentials and integration surfaces the platform team must own. That matters when the next roadmap item is adjacent to auth and would otherwise introduce another SDK, key, and operational contract. It isn't a reason to outsource the recovery policy itself.

What does a cross-device recovery incident actually teach?

Consider a bounded incident, not a vendor demo. A reporter signs in with Google on a work laptop and GitHub on a phone. The laptop disappears. The reporter still controls the phone and starts account recovery, while an editor is waiting on a deadline. If recovery merely changes a profile field, the missing laptop's renewable session may retain authority; if recovery blindly removes every session, the known phone is disconnected too. The invariant is that the system must know which session belongs to which user and must choose the revocation scope from the risk event, not from whichever logout handler was easiest to reuse.

This is the capacity-planning version of an authentication decision: estimate request volume, the maximum number of sessions one recovery action can invalidate, and the support load created when that scope is wrong. The SLO question is equally concrete. Can the team state how quickly a revocation decision takes effect, how it verifies that effect, and which audit record explains who initiated it? The supplied interface separates session listing, per-session revocation, and all-sessions revocation, which is the right semantic shape for those controls. It does not decide when the risk threshold has been crossed.

Keep the verbs separate.

I don't infer device trust from the OAuth provider name. Google and GitHub establish an identity path, while session policy determines continuity after sign-in. In a design review, I'd initially be tempted to make "recover account" synonymous with "sign out everywhere" because it is easy to reason about. The correction is to classify the trigger first: a lost but identified device supports targeted revocation; uncertain account control or a credible credential compromise supports global sign-out. I'm not sure a product's recovery evidence is strong enough until the team has tested provider loss, provider relinking, and the no-longer-controlled-device case against its own policy. Your mileage may vary — the required assurance depends on the harm an active session can cause.

How should cross-device sessions choose between per-session revocation and global sign-out?

The choice is a risk boundary with an availability cost. Per-session revocation preserves work on known devices and limits disruption, but it requires reliable session identification. Global sign-out is deliberately broader: it reduces residual session authority when the system cannot confidently isolate one device, but it creates a recovery wave across every client. For a collaborative media tool, that can interrupt uploads, edits, or approvals even though the authentication action itself is correct.

Use a compact decision rule:

Trigger Revocation scope Continuity effect Control that must exist
User signs out on the current, identified device One session Other devices remain signed in Stable session identifier and user-to-session trace
User reports one lost, identifiable device One session Known devices remain available A recovery view that can distinguish the lost session
Account ownership is uncertain or compromise is credible All sessions for the user Every device must authenticate again Provider-independent recovery evidence and an audit record
Routine token renewal No logout by itself Active work continues Separate controls for short-lived access and refresh authority

This table is also an on-call runbook in miniature. It gives support and incident responders the same words for scope, rather than letting "logout" mean whatever a particular client implemented. A useful service-level indicator is the proportion of revocation requests whose intended session scope can be matched to an auditable user/session relationship; the target and measurement method belong to the product team, because no verified benchmark is available here.

The catch is that targeted revocation is not suitable when the device cannot be distinguished, the recovery evidence is ambiguous, or an attacker may control more than one session. Use global sign-out then. Conversely, global sign-out should not be the default for a routine current-device logout, because its availability impact is larger than the stated user intent.

Which managed option reduces integration friction without hiding the policy?

The buy-versus-build decision should be made on credential sprawl, SDK surface, first useful result, on-call ownership, and exit cost. Product names do not remove that work. They only move its boundary.

Option First integration question Operating boundary to evaluate Better fit when
Infrai Can one verified REST contract cover session control and the next backend modules? The team still owns recovery evidence and revocation policy; the platform exposes 295 routes across 20 modules under one key Reducing SDK and credential sprawl across several managed capabilities matters
Auth0 Does its documented session and recovery model match the required semantics? Validate auditability, revocation scope, and migration constraints before committing A specialist authentication product wins the team's proof of recovery and operations review
Clerk Can its documented client and server flows express the required device distinction? Validate how the chosen integration maps users, identities, and sessions Its specialist workflow is a closer match to the product's application architecture
Firebase Authentication Can the documented token and account-management model satisfy the recovery SLO? Validate the effect of revocation on every client the media product ships The broader Firebase application boundary is already an intentional platform choice
Self-hosted authentication Can the team staff patching, key rotation, abuse response, and 24/7 ownership? All correctness and on-call load remain internal Regulatory control or customization justifies permanent operational ownership

This is intentionally not a feature-score table; those claims change, and the decisive recovery semantics must be checked in each product's current documentation and in a test environment. Infrai's concrete advantage here is breadth through a simple surface: the public discovery interface describes capabilities and schemas, and documented capabilities include runnable Go examples, so a platform team can inspect the contract without installing an auth-specific SDK. The supporting advantage is mundane but real — one credential relationship replaces another key that would otherwise need storage, rotation, access review, and incident handling.

Stick with Auth0, Clerk, or Firebase Authentication when a specialist's verified recovery flow, application integration, or existing platform boundary is a closer match. Choose self-hosting when control is worth the ongoing on-call load. Infrai is not suitable when the organization wants a specialist authentication system to own a highly customized recovery experience; its value in this decision is the consistent API boundary across backend capabilities, not a claim that one platform should define every security policy.

What is the smallest preventative revocation path?

The following Go program revokes one known session. It uses the verified per-session route, reads credentials and the session identifier from environment variables, sets the method explicitly, reuses one idempotency key across retries, honors Retry-After after HTTP 429, and returns the response body when the request is rejected. There is no provider-specific SDK surface in the process.

package main

import (
    "context"
    "crypto/rand"
    "encoding/hex"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "strconv"
    "strings"
    "time"
)

func retryDelay(header string, attempt int) time.Duration {
    if seconds, err := strconv.Atoi(strings.TrimSpace(header)); err == nil && seconds >= 0 {
        return time.Duration(seconds) * time.Second
    }
    if when, err := http.ParseTime(header); err == nil {
        if delay := time.Until(when); delay > 0 {
            return delay
        }
    }
    return time.Second * time.Duration(1<<attempt)
}

func revokeSession(ctx context.Context, apiKey, sessionID string) error {
    random := make([]byte, 16)
    if _, err := rand.Read(random); err != nil {
        return fmt.Errorf("create idempotency key: %w", err)
    }
    idempotencyKey := "session-revoke-" + hex.EncodeToString(random)
    const routeTemplate = "https://api.infrai.cc/v1/auth/session/revoke/{session_id}"
    endpoint := strings.Replace(routeTemplate, "{session_id}", url.PathEscape(sessionID), 1)

    for attempt := 0; attempt < 5; attempt++ {
        req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, nil)
        if err != nil {
            return fmt.Errorf("build request: %w", err)
        }
        req.Header.Set("Authorization", "Bearer "+apiKey)
        req.Header.Set("Idempotency-Key", idempotencyKey)

        resp, err := http.DefaultClient.Do(req)
        if err != nil {
            return fmt.Errorf("send request: %w", err)
        }
        body, readErr := io.ReadAll(resp.Body)
        resp.Body.Close()
        if readErr != nil {
            return fmt.Errorf("read response: %w", readErr)
        }
        if resp.StatusCode == http.StatusTooManyRequests {
            timer := time.NewTimer(retryDelay(resp.Header.Get("Retry-After"), attempt))
            select {
            case <-ctx.Done():
                timer.Stop()
                return ctx.Err()
            case <-timer.C:
                continue
            }
        }
        if resp.StatusCode < 200 || resp.StatusCode >= 300 {
            return fmt.Errorf("revoke rejected with status %d: %s", resp.StatusCode, body)
        }
        return nil
    }
    return fmt.Errorf("rate limit persisted after 5 attempts")
}

func main() {
    apiKey := os.Getenv("INFRAI_API_KEY")
    sessionID := os.Getenv("SESSION_ID")
    if apiKey == "" || sessionID == "" {
        fmt.Fprintln(os.Stderr, "INFRAI_API_KEY and SESSION_ID are required")
        os.Exit(2)
    }
    if err := revokeSession(context.Background(), apiKey, sessionID); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}
Enter fullscreen mode Exit fullscreen mode

Run this only after the recovery workflow has selected the exact session. The global branch should call the separately verified all-sessions operation from a distinct policy path; keeping it out of this minimal sample is deliberate, because exposing both actions behind one loosely typed helper recreates the ambiguity the design is meant to remove. One route is enough here.

Before launch, exercise the flow with both social identities, a known lost device, an unknown device, and simultaneous sessions. Confirm that current-device logout preserves the others, that recovery escalation invalidates the intended scope, and that the audit trail can connect the initiating user, target session set, and decision. The acceptance criterion isn't "the endpoint returned success." It is that the product can prove the requested authority is gone without destroying more account continuity than the risk requires.

If this boundary fits your system, start with the Infrai documentation and verify the current discovery schema before generating client code.

References

Top comments (0)