Skip to content
DomainKit
Esc
navigateopen⌘Jpreview
On this page

Connect Cloudflare

Connect a Cloudflare account with OAuth or a scoped API token, discover the account from a known zone, and persist the resulting authorization on your server.

Cloudflare supports standards-based OAuth and API-token connection methods. Both produce the same durable DomainKit authorization aggregate and provider context.

Run every step in this guide on a trusted server. Do not construct a Cloudflare provider client or retain the token in browser code.

Prerequisites

  • A durable AuthorizationLifecycle.Repository for authorization aggregates and credentials.
  • A short-lived, one-time continuation store for OAuth.
  • Authenticated start and callback routes bound to your host user and tenant.
  • A known customer domain whose authoritative zone the credential must be able to read.
  • For OAuth, a registered Cloudflare client, redirect URI, client secret, and the scope IDs assigned to that client.

Start OAuth

import {
  Cloudflare,
  Connection,
  DnsRecord,
  DomainName,
  Secret,
  Verification,
} from "domainkit/promise";

const capabilities = ["dns:read", "dns:write"] as const;

const flow = Cloudflare.Auth.oauthFlow({
  capabilities,
  client: {
    clientId,
    clientSecret: Secret.make(clientSecret),
  },
  clientAuth: "client_secret_basic",
  domain: DomainName.parse("example.com"),
  redirectUri,
  scopes: scopeIds,
});

const result = await Connection.start({
  authorizedById,
  grant: {
    _tag: "domains",
    domains: [DomainName.parse("example.com")],
  },
  method: Connection.Method.Interactive({ continuations, flow }),
  ownerId,
  repository,
});

An interactive start returns Redirect. Send its authorizationUrl to the browser only after the host has checked the current session and tenant authority.

The flow uses the known domain to resolve the selected Cloudflare account after authorization. The customer does not need to find or type an account ID.

Complete the callback

Use the same flow configuration on the authenticated callback route:

const connected = await Connection.complete({
  callbackUrl: new URL(request.url),
  continuationId,
  continuations,
  flow,
  repository,
});

Connection.complete consumes the continuation exactly once, validates the OAuth response, discovers the account from the known zone, and commits the provider authorization. Persist only the non-secret provider context in ordinary application records; the credential belongs behind the repository’s secret-storage boundary.

Connect an API token

Use a token route when the customer supplies a scoped Cloudflare API token:

const domain = DomainName.parse("example.com");
const method = Cloudflare.Auth.tokenConnectionMethod({
  capabilities,
  domain,
  token: Secret.make(apiToken),
});

const connected = await Connection.start({
  authorizedById,
  grant: { _tag: "domains", domains: [domain] },
  method,
  ownerId,
  repository,
});

Ask for the narrowest token that covers the intended zones and DNS permissions. Domain-targeted validation supports user-owned and account-owned API tokens and discovers the account from the visible zone.

Reconstruct the provider

After loading and decoding the stored credential and provider context, construct the account-scoped provider on the server:

const provider = Cloudflare.make({
  accountId: authorization.providerAccountId,
  capabilities: authorization.requiredCapabilities,
  token: credential.accessToken,
  tokenKind: context.tokenKind,
});

Pass that provider directly through the Promise APIs’ provider fields for planning, apply, and receipt-bound cleanup. Authoritative observation also needs the zone-tagged verification wrapper:

const zone = DomainName.parse("example.com");
const record = DnsRecord.parse({
  _tag: "TXT",
  metadata: {
    ownership: "customer",
    provenance: "example",
    purpose: "domain verification",
  },
  name: "_verify.example.com",
  policy: "append",
  ttl: 300,
  value: "verification-value",
});

await Verification.observe({
  provider: Verification.Provider.Enabled({ provider, zone }),
  record,
});

Account discovery outcomes

  • One matching visible zone identifies the Cloudflare account and can continue.
  • No matching zone is an actionable authorization or zone-ownership failure.
  • Ambiguous provider accounts require host/user selection; never choose an opaque account ID silently.

Cloudflare’s non-mutating token verification response does not always enumerate DNS permissions. DomainKit records declared, introspected, and exercised capability evidence instead of treating scope strings as complete proof.

Next steps

Was this page helpful?