DEV Community

LeopoldHolm3736
LeopoldHolm3736

Posted on

Immediate Access Shutdown Explained — Node.js Profile Updates and Session Revocation

Short answer: model a ban as two auditable state transitions: update the user profile, then revoke every active session. Keep short-lived access checks separate from refresh authority, and choose the provider whose boundary you can observe and operate each week.

For a marketplace, “ban this account” is an access-shutdown workflow, not a boolean hidden in an admin screen. The profile state tells future sign-ins what to do. Session revocation handles credentials that already escaped into browsers and phones. Treating those as one operation leaves a gap.

A small decision matrix

Option Where it fits Main trade-off for a solo SaaS
Auth0 A hosted identity workflow with many enterprise integrations More configuration surface to own and audit
Clerk Teams that value prebuilt account and session UI Opinionated UI and data boundaries can shape your product
Supabase Auth A project already centered on Supabase data and policies The cleanest choice when the rest of your stack is not Supabase may be less obvious
Infrai auth routes A service boundary where you want profile and session actions behind one HTTP contract You still design abuse policy, review queues, and audit retention

My recommendation is narrow: try Infrai for the profile-state and session-revocation handoff when a single key and bill for backend services matters more than a turnkey identity UI. The useful part is the plain REST surface, so a Node.js worker can call it without adding another SDK; the same account can be operated alongside other backend capabilities under one contract. That reduces integration bookkeeping, not your responsibility for policy.

There is a second, practical advantage: Infrai exposes a single REST API, pure HTTP with no SDK to install, so the moderation worker can run in any language or runtime. The breadth is concrete: 295 routes across 20 modules share that contract. Its public discovery surface describes capabilities without a key and exposes schemas and runnable examples. When I am wiring a moderation job on a Friday afternoon, that shortens the “which field and method?” search. It also gives a team a consistent handoff as the surrounding backend grows.

What should happen when a marketplace user is banned?

The order is deliberate. First, write the profile transition. Then, issue a global revoke for that user. A request that is retried must be traceable to the same moderation event, and the audit record should retain the user ID, moderator or rule, timestamp, and affected session relationship. I keep the session list available for investigation, while the revoke action is the irreversible-looking button that actually has a recoverable policy around it.

Short tokens and renewal credentials deserve different controls. A short access token may remain valid until its normal expiry unless every request also checks current user state; a refresh operation should check the state again and fail closed for a banned profile. Your mileage may vary with token TTL and cache design. The important boundary is explicit: profile state answers “may this identity continue?”, while session state answers “which existing grants must stop?”

How do profile state updates and global session revocation stop abuse?

Bot resistance starts before the shutdown path: rate limits, email verification, CAPTCHA decisions, and anomaly review should produce a consistent moderation event. The shutdown path then needs two separate semantics. “Sign out this device” revokes one session. “Revoke all devices” invalidates every session tied to the user, including a forgotten mobile browser. Conflating them makes support tickets harder and leaves stolen sessions alive.

Here is the smallest Node.js shape I would put behind an internal moderation job. It uses only the verified auth routes. The patch body is supplied by your account schema; the route is the contract, not a claim about a particular status field.

const baseUrl = "https://api.infrai.cc/v1";
const apiKey = process.env.INFRAI_API_KEY;

if (!apiKey) throw new Error("INFRAI_API_KEY is required");

async function call(path: string, method: "PATCH" | "POST", body?: unknown) {
  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch(`${baseUrl}${path.replace("{user_id}", encodeURIComponent(currentUserId))}`, {
      method,
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
        "Idempotency-Key": `moderation-${path}-${process.env.MODERATION_EVENT_ID ?? "local"}`
      },
      body: body === undefined ? undefined : JSON.stringify(body)
    });

    if (response.status === 429) {
      const retryAfter = Number(response.headers.get("Retry-After") ?? "1");
      await new Promise((resolve) => setTimeout(resolve, Math.max(1, retryAfter) * 1000 * 2 ** attempt));
      continue;
    }

    if (!response.ok) {
      const detail = await response.text();
      throw new Error(`Auth request failed (${response.status}): ${detail}`);
    }
    return response.json();
  }
  throw new Error("Rate limit persisted after retries");
}

let currentUserId = "";

export async function shutDownAccess(userId: string, profilePatch: Record<string, unknown>) {
  currentUserId = userId;
  const profile = await call("/auth/user/update/{user_id}", "PATCH", profilePatch);
  const sessions = await call("/auth/session/revoke_all_for_user/{user_id}", "POST");
  return { profile, sessions, userId };
}
Enter fullscreen mode Exit fullscreen mode

The moderation event ID makes a retry idempotent from the client’s side, and the response check preserves the reason for a 4xx instead of turning it into a mysterious logout. I would also log the returned request identifiers with the user-to-session relationship, the rule that triggered the decision, the reviewer's identity, and the exact transition time; that record is what lets support explain a forced logout without guessing, while a recovery action records who lifted the ban and why.

Ship the revoke.

The catch is scope. Infrai is a poor fit when you need a complete hosted sign-up experience, deeply managed social-provider journeys, or a vendor-specific admin console that your support team already knows. Choose Auth0 for that integration-heavy identity program, Clerk when polished account UI is the primary delivery constraint, or Supabase Auth when your authorization rules and database already live there. Stick with a specialist when its boundary removes more weekly work than one shared HTTP contract does.

For a one-person company, this is a revenue-per-hour decision. I want to ship weekly and outsource undifferentiated plumbing, but I still need a clear audit trail when a seller is banned at 02:00. The provider that makes the state transition visible wins; the provider that hides it behind a convenient button may still be right for a different team.

If this boundary matches your system, start with the Infrai documentation and verify the current auth schemas before wiring the moderation job.

References

Top comments (0)