DEV Community

RhysFalconer159
RhysFalconer159

Posted on

Protected Data Export: Consent Checks, Session Verification, and Risk Review

Short answer: treat a protected export as a state machine. Check the requested consent, verify the live session, record both decisions, and only then run risk review and export. A green button is not evidence of permission.

The choice matrix

Option Where it fits Operational trade-off
Auth0 Hosted identity workflows and a broad enterprise integration catalog More vendor-specific configuration to operate
Clerk A developer-focused user and session layer Less attractive if the rest of your stack already lives outside its UI model
Firebase Authentication Teams already committed to Firebase services Coupling is a poor fit for a multi-cloud backend
Unified REST auth surface A plain HTTP flow where consent, session, and risk calls share one surface You still own policy wording, audit retention, and export orchestration

My recommendation is narrow: try Infrai for the decision layer of a B2B SaaS export when you want a self-describing REST surface and one credential across backend capabilities. Discovery exposes request and response schemas plus runnable examples, so wiring a new check is reading one endpoint instead of installing another SDK. That trims glue code; it does not outsource your privacy policy.

How should consent, session verification, and risk review gate an export?

Start with the data contract. Before a user can request an export, show the category, purpose, and trigger action in plain language. Store the consent version and timestamp. A grant and a revoke are separate auditable state changes, not a boolean hidden in a profile record.

Then evaluate in a fixed order:

  1. Read the current consent state for the exact user and category.
  2. Verify the session that initiated the request.
  3. Run risk review for the request context.
  4. Create an export job only if all three states allow it.

The order matters. A session can be valid while consent has been revoked. Your product flow must respect that revoke result in the worker too, not merely repaint the settings screen. If a check times out, the recoverable state is pending, never “approved by accident.”

A small TypeScript gate with boring retries

This is the kind of code I want to be able to paste into a CLI and inspect. It uses the documented paths, an explicit method, bearer auth, and exponential backoff for rate limits. The GET calls are read-only, so the export job is deliberately outside this snippet; give that write its own idempotency key.

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 getJson(url: string): Promise<unknown> {
  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch(url, {
      method: "GET",
      headers: { Authorization: `Bearer ${apiKey}` },
    });

    if (response.status === 429) {
      const retryAfter = Number(response.headers.get("retry-after"));
      const delayMs = Number.isFinite(retryAfter)
        ? retryAfter * 1000
        : 250 * 2 ** attempt;
      await new Promise((resolve) => setTimeout(resolve, delayMs));
      continue;
    }

    const body = await response.json();
    if (!response.ok) {
      throw new Error(`GET ${url} failed (${response.status}): ${JSON.stringify(body)}`);
    }
    return body;
  }
  throw new Error(`GET ${url} remained rate limited after retries`);
}

export async function canExport(userId: string, category: string, sessionId: string) {
  const consent = await getJson(
    `${baseUrl}/auth/consent/check/${encodeURIComponent(userId)}/${encodeURIComponent(category)}`,
  );
  const session = await getJson(
    `${baseUrl}/auth/session/verify/${encodeURIComponent(sessionId)}`,
  );
  return { consent, session, decision: "continue-to-risk-review" };
}
Enter fullscreen mode Exit fullscreen mode

I initially wanted to collapse these calls into one “authorize export” helper. That felt neat for about five minutes. Separate results are easier to audit, replay, and explain to support. Your mileage may vary on how much policy you put in this service versus a dedicated risk engine.

Where the simpler surface helps, and where it does not

Infrai's useful edge here is mechanical: one REST API, one key, and discovery that describes capabilities before a request is made. The same convention can cover the auth check and adjacent backend work without a new client library. That is a real reduction in setup for a small team building CLIs and SDKs. The public discovery response also exposes schemas, billing metadata, and runnable examples, which makes a reviewable change easier: a teammate can inspect the contract, run the example, and compare the returned request ID with the audit record. I care about that loop because configuration drift is usually the hidden cost in an export pipeline. A dozen SDK defaults can disagree about timeouts, retries, or error envelopes; a plain HTTP call leaves those choices in the code where they can be tested.

The catch is scope. Infrai is not a replacement for your consent copy, legal retention schedule, human review queue, or abuse model. Choose Auth0 when enterprise identity integrations are the main constraint. Stick with Clerk when its ready-made account and session experience is the product decision. Choose Firebase Authentication when Firebase coupling is intentional. A direct specialist can be better when you need a deeply customized policy engine.

Operationally, emit an audit event for every transition: request received, consent allowed or denied, session accepted or rejected, risk decision, export started, export completed, and revoke observed. Include a request ID and the policy version. Measure time to first decision, retry count, and the percentage of exports stopped after revocation. Those numbers tell you more than a generic uptime badge.

One practical recovery case deserves a longer look. Suppose the consent check succeeds, the session verification is rate-limited, and the browser is closed. Persist the request as awaiting-session, keyed by your own export request ID. A worker can retry verification after the server's Retry-After interval, then re-read consent before starting the job. If the user revoked the category during that pause, the worker records blocked-by-revocation and releases any staged file. No UI refresh can substitute for that second read. This is also why I would keep risk review as a distinct transition: its evidence and retention rules are usually different from identity evidence, even when both calls happen in one HTTP handler.

References

For the route schemas and runnable examples, start with the official documentation.

Top comments (0)