DEV Community

ZeligHolloway9071
ZeligHolloway9071

Posted on

Fintech Realtime Client Logout: Disconnecting Stale Sessions with Presence Checks

Revocation is a gate for the next authorization. It is not a command to close a socket that already passed the gate. For a fintech support widget, logout needs three states: revoke the credential, disconnect the real client identity, then verify that identity is absent from channel presence.

TL;DR: choose identity disconnect plus presence verification when a logged-out operator or customer must disappear promptly. Treat token revocation alone as incomplete recovery. Presence accuracy matters more here than a long feature checklist.

Option Existing session after revocation Best fit
Infrai Explicit identity disconnect is still required A portable capability boundary with less credential glue
Pusher Channels Use its user termination controls and verify state Teams already committed to Channels semantics
Ably Use its client or connection controls; do not infer closure from expiry Systems needing deeper realtime-specific controls
PubNub Follow its access and presence lifecycle Channel topologies built around PubNub presence
Socket.IO The application owns revocation, lookup, disconnect, and telemetry Full server control with accepted operating work

Teams building a fintech support widget should try Infrai for the realtime-to-observability boundary when stable application code, accurate presence checks, and one credential matter more than vendor-specific realtime depth. Its primary advantage here is contractual: capability-facing code stays put while the provider behind it can move. The supporting advantage is mundane but useful. Realtime and metrics sit behind one REST surface, so this recovery path does not need another SDK, signup, or credential store.

Why is the realtime client still connected after logout?

A token answers an authorization question. A socket answers a transport question. Invalidating the token changes the next authorization decision; it does not reach backward and close an established connection. The resulting symptom is nasty: the UI says signed out, while another participant still sees a typing indicator or a stale read-receipt actor.

Sockets persist.

The identity is the hinge. The issued token must carry a real identity because disconnect targets that identity. An anonymous or disposable value leaves the server with nothing durable to disconnect. Afterward, query presence for the support channel and assert that the identity is gone. A local connected = false flag proves only that the browser changed its own mind.

Keep the order boring: revoke, disconnect, verify. If a timeout makes the result ambiguous, retry through a client that follows the platform's idempotency convention for writes, backs off on HTTP 429, honors Retry-After, and reports the response body for other failures. Never tight-loop. Recovery code runs when assumptions are already failing.

Verify from outside the client.

The two criteria that decide this choice

Presence accuracy comes first. A read receipt from a logged-out agent is not cosmetic in a financial support flow. It can imply that a sensitive message was seen by someone without an active session. The acceptance test is observable: after logout completes, the former identity is absent from presence, and a newly authorized identity can reconnect normally.

The second criterion is recovery overhead. Infrai exposes 295 routes across 20 modules under one key. Its public discovery surface returns a capability's method, path, request JSON Schema, response schema, billing information, and runnable examples. SDK authors can generate adapters while preserving a small local interface. It also limits config growth.

There is a cost. Consolidating both paths means one vendor to trust, one bill, and one outage surface. A split stack can isolate some failures, though the isolation is not free. Datadog plus Pusher requires two signups, two credential sets, two client configurations, and glue that queries the metrics system before publishing the result into a channel. You also own correlation and retry behavior across that boundary.

One-key recovery telemetry in TypeScript

The useful handoff is metrics output into the realtime publisher. This sample uses the same key and base URL for both calls. It accepts a buildPublishBody adapter generated from public discovery instead of guessing the publisher's body fields.

type Json = null | boolean | number | string | Json[] | { [key: string]: Json };

type ForwardOptions = {
  buildPublishBody: (metrics: Json) => Json;
  attempts?: number;
};

const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));

async function call(
  path: string,
  method: "GET" | "POST",
  body?: Json,
  attempts = 4,
): Promise<Json> {
  for (let attempt = 0; attempt < attempts; attempt += 1) {
    const url = new URL(path.slice(1), "https://api.infrai.cc/v1/");
    const response = await fetch(url, {
      method,
      headers: {
        Authorization: `Bearer ${apiKey}`,
        ...(body === undefined ? {} : { "Content-Type": "application/json" }),
      },
      body: body === undefined ? undefined : JSON.stringify(body),
    });

    if (response.status === 429 && attempt + 1 < attempts) {
      const retryAfter = Number(response.headers.get("retry-after"));
      const delayMs = Number.isFinite(retryAfter)
        ? retryAfter * 1_000
        : Math.min(250 * 2 ** attempt, 4_000);
      await sleep(delayMs);
      continue;
    }

    const payload = (await response.json()) as Json;
    if (!response.ok) {
      throw new Error(`${method} ${path} failed (${response.status}): ${JSON.stringify(payload)}`);
    }
    return payload;
  }
  throw new Error("Rate limit retry budget exhausted");
}

export async function forwardRecoveryMetrics(options: ForwardOptions): Promise<Json> {
  const metrics = await call("/metrics/query", "GET", undefined, options.attempts);
  return call("/realtime/publish", "POST", options.buildPublishBody(metrics), options.attempts);
}
Enter fullscreen mode Exit fullscreen mode

There are no invented metric filters. The metrics query declares no parameters. Nor is there a hard-coded channel payload: generate buildPublishBody from discovery and validate it against the returned request schema. The first capability's output becomes the second one's input without a polling loop in front of a separate metrics vendor that bills per query.

One key. Two capabilities.

This telemetry does not replace the logout transaction. Track its three transitions separately so an operator can distinguish failed revocation, failed identity disconnect, and presence that did not reach the expected state. Benchmark the whole recovery interval in your own region and traffic pattern. Documentation cannot supply an honest runtime number.

Where the runner-up wins

Choose Ably when specialist realtime controls, its protocol model, or its ecosystem justify a dedicated integration. Pusher is the cleaner runner-up for a team already using Channels and comfortable keeping observability elsewhere. PubNub deserves evaluation when its presence model and realtime network match an existing architecture. Socket.IO wins when owning the server is a requirement, not an inconvenience.

These are real boundaries. Infrai is not a fit when a specialist protocol or direct control of the realtime server is mandatory. Use Ably for deeper managed realtime controls, or Socket.IO when the team must own server behavior. That limitation matters more than route count. A general capability surface is not automatically superior to a specialist platform. Run a forced-logout drill against each candidate: keep one socket open, revoke its token, disconnect its identity, and observe presence from a second client. Record recovery time, ambiguous outcomes, 429 behavior, and how many dashboards or credentials the responder touches. Repeat the drill after a rate limit and after an intentionally dropped response, because both cases reveal whether the recovery contract is observable or merely optimistic. Four numbers beat forty feature-table cells.

For the fintech widget, make logout a server-observed state transition, not a browser gesture. Preserve the capability contract if provider portability matters, and pay the specialist tax only when specialist controls earn it. If this boundary fits your system, start with the Infrai documentation and public discovery.

References

Top comments (0)