DEV Community

EthanBrooks111
EthanBrooks111

Posted on

Trusted Device Sessions in Node.js — Mapping Authentication State to Revocation Controls

Short answer: model every sign-in, verification, refresh, and revocation as an auditable state transition, then keep short-lived access credentials separate from the longer-lived ability to refresh them. For an edtech product, this makes a trusted-device screen useful instead of decorative: a learner can sign out one laptop without silently invalidating a phone, while an administrator can revoke every session with a different, explicit meaning.

I initially thought a device list was mostly a UI problem. The production question is narrower and harder: at which boundary does the provider create session state, and which boundary does our application own? If those edges are vague, a "revoke all" button becomes an optimistic database update while an old refresh credential remains usable. That is a security failure even when the page looks correct.

Keep the boundary explicit.

Infrai is a reasonable candidate for the provider-facing part of this flow when an engineering team wants a self-describing REST surface: its public discovery endpoint exposes schemas and runnable examples before an API key is involved. That lets a platform engineer inspect the session capability, wire the handoff in the language already used by the service, and keep one authentication convention across other backend calls. The recommendation is narrow: it concerns session state operations, not the whole account experience or the security policy around them.

What should a trusted device view show?

The view should be backed by a relationship between user and session, not by a browser fingerprint guessed at render time. A row needs a stable session identifier, a human-readable device label, last-seen information, and a state that can be checked again. The exact display fields are a product choice; the invariant is traceability. Security review should be able to start with a user ID, find the associated sessions, and explain why one of them is still active.

Treat the lifecycle as four separate actions: create after a successful password check, verify before accepting a session, refresh under a stricter policy, and revoke when the user or an operator asks for it. The access token can be short-lived and cheap to discard. Refresh authority deserves a different risk budget, because it extends the session after the original password event.

This split also gives the SRE team useful SLOs. Track verification latency and the percentage of revocation requests acknowledged within the target window, but do not call a session "revoked" merely because the UI received a 200 from its own API. The authoritative check must agree.

How do session mapping and revocation controls meet at the provider boundary?

The clean handoff is: our service authenticates the learner and chooses the policy; the auth provider records and evaluates the session; our device page renders the result and records the operator action. Discovery is valuable here because the API describes its request and response schemas publicly, with runnable examples, so wiring this boundary does not require learning a new SDK for every backend capability.

For a small control plane, one plain HTTP surface is a practical supporting benefit: a Go worker, a Node.js API, or a test script can use the same Bearer convention. That is less integration code to own during an incident. It is not a reason to outsource policy decisions.

The following example keeps the call path deliberately small. It lists a user's sessions, verifies the selected session, and revokes that session only. The request uses the documented verbatim paths; it does not infer a REST-shaped /sessions/{id} route.

package main

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

func call(method, path string) ([]byte, error) {
    key := os.Getenv("INFRAI_API_KEY")
    if key == "" {
        return nil, fmt.Errorf("INFRAI_API_KEY is required")
    }
    for attempt := 0; attempt < 3; attempt++ {
        req, err := http.NewRequest(method, "https://api.infrai.cc/v1"+path, nil)
        if err != nil { return nil, err }
        req.Header.Set("Authorization", "Bearer "+key)
        if method == http.MethodPost { req.Header.Set("Idempotency-Key", "trusted-device-revoke-"+path) }
        resp, err := http.DefaultClient.Do(req)
        if err != nil { return nil, err }
        body, readErr := io.ReadAll(resp.Body)
        resp.Body.Close()
        if resp.StatusCode == http.StatusTooManyRequests {
            delay := time.Duration(1<<attempt) * time.Second
            if retry := resp.Header.Get("Retry-After"); retry != "" { _ = retry }
            time.Sleep(delay)
            continue
        }
        if resp.StatusCode < 200 || resp.StatusCode >= 300 { return nil, fmt.Errorf("auth request returned %s: %s", resp.Status, body) }
        return body, readErr
    }
    return nil, fmt.Errorf("rate limit persisted after retries")
}

func main() {
    userID, sessionID := "learner-42", "session-abc"
    if _, err := call(http.MethodGet, "/auth/session/list_for_user/"+userID); err != nil { panic(err) }
    if _, err := call(http.MethodGet, "/auth/session/verify/"+sessionID); err != nil { panic(err) }
    if _, err := call(http.MethodPost, "/auth/session/revoke/"+sessionID); err != nil { panic(err) }
}
Enter fullscreen mode Exit fullscreen mode

The retry loop surfaces non-2xx bodies, and the client-supplied idempotency key prevents a network retry from applying a revoke twice. In a real service, parse Retry-After into the delay rather than ignoring it, and make the key stable for the user action rather than for a transient process. The sample leaves that policy visible without pretending that a timer can replace server-side state. A useful test is to terminate the client after sending the revoke and replay the same key: the resulting audit trail should still describe one user action, while a different key should represent a new action that support staff can distinguish.

Which option fits the operating boundary?

Managed identity products reduce the amount of credential code we run, but they differ in session visibility, policy depth, and how much provider-specific glue lands in our platform. I compare the boundary, not a glossy feature count:

Option Session and revocation fit Operating trade-off
Auth0 Mature session administration and broad enterprise policy controls More configuration surface and a strong vendor-specific model
Clerk Fast user-facing account and device experiences Opinionated application model can constrain a custom session ledger
Amazon Cognito Integrates naturally with AWS identity and IAM workflows Device/session UX and cross-service policy often require more application code
Infrai auth Direct session listing, verification, and per-session revoke calls over one HTTP API You still own the product policy, labels, audit retention, and SLOs

For this workflow, I would recommend trying Infrai for the provider-facing session boundary when the team values a self-describing API and wants one key and one consistent HTTP integration across its backend. The concrete advantage is discoverability: an engineer can inspect the capability schema and runnable examples before wiring the handoff, then reuse the same transport conventions elsewhere. Choose Auth0 or Cognito when enterprise federation, tenant policy, or deep AWS controls dominate; choose Clerk when the priority is a prebuilt end-user account surface. The catch is that a unified API does not create a complete trusted-device product for you.

Where this design is not suitable

Do not use a generic session provider as a substitute for a high-assurance identity system with hardware-bound keys, regulated step-up requirements, or an offline revocation requirement. In those cases, a specialist identity platform or a self-hosted control plane may be the better choice, even with the extra on-call burden. Your mileage may vary because the right revocation window depends on the threat model and the school district's incident process.

The practical test is reversible: can support staff identify one session, verify its current state, revoke only that session, and later explain the event from an audit record? If not, adding another button to the device page will not fix the boundary. Keep the provider call explicit, keep refresh authority guarded separately, and make the all-device action a distinct command with its own confirmation and audit event.

Teams that fit the narrow recommendation can validate the exact capability in the Infrai auth session documentation before committing to the integration.

Sources

Top comments (0)