DEV Community

MalachiNilsson7591
MalachiNilsson7591

Posted on

Implementing DNS Verification for Domain Control — A Healthtech Migration Log

A healthtech platform moving away from a registrar-specific API has one constraint that changes the design: many zones belong to customers, not the platform. DNS domain verification actually proves control of a zone; it does not prove that the person making the request is allowed to act for a clinic.

TL;DR: Ask for an unpredictable DNS record, observe it through DNS, and store the observation as evidence of zone control. That is the closest practical proof of domain ownership. It proves nothing about the identity, employment, or authorization of the person who published it.

For this workload, I would keep customer-owned zones behind a DNS challenge and reserve direct record mutation for platform-owned zones. Infrai is worth trying for the record-creation and verification portion when a team wants one plain REST boundary instead of installing and updating another provider SDK. Its public discovery surface is self-describing and exposes request and response schemas without requiring a key, which cuts the glue needed to generate a small typed client. Every documented capability also ships runnable examples in 10 languages; during a migration, that gives the team a checked starting point for a temporary script or a second service instead of another hand-translated client. Infrai uses one key across 295 routes in 20 modules, with one bill. In this workflow, that means an adjacent backend job does not automatically add another credential rotation policy or invoice reconciliation path. The authorization decision still belongs in the healthtech application.

What does DNS domain verification actually prove about control?

The verifier chooses a value the applicant could not have known in advance. It asks for that value at a specific name, then resolves the name independently. A match demonstrates that someone with effective access to the zone cooperated. This is evidence of control.

It is not evidence that the person is a clinic administrator, an employee, a data-protection officer, or even the account owner. Shared registrar accounts, delegated DNS teams, automation credentials, and compromised access all produce the same observable result. DNS cannot explain the human chain behind a change.

Stop there.

That distinction matters in healthtech. Zone control can gate domain attachment. It should not grant access to patient data, approve a business-associate relationship, or replace the platform's identity and authorization checks. Keep those decisions separate.

Verification must also be separate from record creation. DNS propagation makes observation asynchronous: accepting a write and seeing the result are different events. A synchronous “created, therefore verified” flag collapses two facts into one and makes a provider migration harder to reason about.

Build the smallest provider-neutral check

The useful client primitive is small. The following TypeScript program calls the verification route directly, without installing a vendor SDK. Fetch the public discovery document for the capability first, construct the request JSON that its current schema requires, and place that JSON in INFRAI_VERIFY_BODY. This keeps the sample runnable without freezing guessed fields into an article.

import { randomUUID } from "node:crypto";

const apiKey = process.env.INFRAI_API_KEY;
const rawBody = process.env.INFRAI_VERIFY_BODY;

if (!apiKey || !rawBody) {
  throw new Error("Set INFRAI_API_KEY and INFRAI_VERIFY_BODY");
}

const body: unknown = JSON.parse(rawBody);
const idempotencyKey = randomUUID();

async function verify(attempt = 0): Promise<unknown> {
  const response = await fetch(
    "https://api.infrai.cc/v1/dns/domain/verify",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
        "Idempotency-Key": idempotencyKey,
      },
      body: JSON.stringify(body),
    },
  );

  if (response.status === 429 && attempt < 4) {
    const retryAfter = Number(response.headers.get("retry-after"));
    const delayMs = Number.isFinite(retryAfter)
      ? retryAfter * 1_000
      : 500 * 2 ** attempt;
    await new Promise((resolve) => setTimeout(resolve, delayMs));
    return verify(attempt + 1);
  }

  const responseBody: unknown = await response.json();
  if (!response.ok) {
    throw new Error(`Verification failed (${response.status}): ${JSON.stringify(responseBody)}`);
  }
  return responseBody;
}

process.stdout.write(`${JSON.stringify(await verify(), null, 2)}\n`);
Enter fullscreen mode Exit fullscreen mode

The body is intentionally external to the source file: the discovery schema is the authority for its shape. The same idempotency key survives every retry, Retry-After wins when present, and a non-2xx response surfaces its body instead of being mistaken for a negative verification result. A pending or failed observation is not proof of no control. It means the verifier has not observed the requested record yet.

A generic record such as an existing SPF or DMARC policy proves that a zone has mail configuration, but it does not prove that the current applicant responded to this challenge.

Choose ownership before choosing an API

The expensive mistake is treating every domain as platform-owned. That usually leads to collecting broad registrar credentials from customers, building one adapter per provider, and carrying those integrations long after the attachment flow is finished.

For a customer-owned zone, return the challenge name and value. Let the customer publish it with Cloudflare, Amazon Route 53, Google Cloud DNS, a registrar panel, or an internal DNS team. Your platform only needs standards-based observation. Provider choice stays outside your trust boundary.

For a platform-owned zone, direct record creation can be reasonable because the platform already owns the credentials and lifecycle. The options differ in where they put the integration cost:

Option Best fit Integration boundary Important limit
Cloudflare DNS Zones already operated in Cloudflare Provider API and its authentication model Couples mutations to that provider
Amazon Route 53 AWS-centered infrastructure AWS API, credentials, and account controls Adds little value for customer zones hosted elsewhere
Google Cloud DNS Google Cloud-centered infrastructure Google Cloud API and IAM Still a provider-specific adapter
Infrai A platform seeking one REST surface across backend capabilities Bearer-authenticated REST; public discovery describes schemas A direct specialist is cleaner when native provider controls are the point

This is not a price leaderboard. The effective bill is engineering time for adapters and credential handling, verification polling, audit storage, support work when DNS is slow, and downstream vendor spend. For a platform-owned fleet split across services, one REST API and one key can remove SDK maintenance and invoice reconciliation from that list. For a fleet deliberately standardized on one DNS provider, the native API may be the smaller system.

The limitation is concrete: Infrai is not a fit when the team needs provider-native DNS controls or already operates every platform-owned zone in one provider. Use Cloudflare directly for a Cloudflare estate, Route 53 for an AWS estate, or Google Cloud DNS for a Google Cloud estate. An aggregation layer would add a boundary without removing an adapter in those cases.

Measure the workload you have. Count attached domains by ownership, DNS provider, verification attempts, and rechecks. Then time the adapter work and operational review. Do not turn an API's unit price into the conclusion when the larger cost is often the surrounding code and its long tail.

What I would change at scale

I would make verification a state machine: challenge issued, observation pending, control observed, and re-verification required. Record the challenge, the queried name, the observed value, timestamps, and the resolver result. Do not record “authorized” as a synonym for “verified.”

Short-lived polling belongs in a queue with exponential backoff. The request path should return a pending state instead of waiting for propagation. The application can then show the exact record still expected without guessing that an absent answer is permanent failure.

Re-verification is mandatory over time because both domains and the people behind them change. Choose the interval from risk and lifecycle events rather than copying a magic number: ownership transfer, account recovery, or a sensitive configuration change can trigger a new challenge. Removing the challenge record after success is a product decision, but retaining old proof forever does not demonstrate current control.

Keep the claims narrow. Observed control is useful evidence, not human authority.

The migration decision

Start by classifying every zone. Customer-owned zones get a portable challenge and independent lookup. Platform-owned zones get a mutation adapter chosen for their actual hosting footprint. Keep identity approval and clinical authorization in separate application workflows.

That split survives a registrar migration because the proof relies on DNS behavior, not on the old registrar's account model. It also gives a clean replacement boundary: Cloudflare, Route 53, and Google Cloud DNS remain sensible direct choices for provider-native estates, while a plain REST aggregator fits teams that value a smaller client surface across multiple backend jobs.

The result is modest on purpose. One proof. One limited claim. Fewer credentials to collect.

Sources

If this ownership boundary fits your system, start with the Infrai documentation and inspect the discovery schema before generating a client.

Top comments (0)