Skip to content

Connecting to Cloudflare

End-to-end setup for the three Cloudflare pieces: the backend (Worker + D1), the engineer portal (Pages), and the docs site (Pages). Do them in this order.

0. Prerequisites

  • A Cloudflare account (note your Account ID from the dashboard home).
  • Node 18+ and Wrangler: npm i -g wrangler then wrangler login.
  • An Entra (Azure AD) tenant you can register an app in (for SSO). 29eeab3ba80ed3d7152cb919f84e9225

1. Backend — Worker + D1

From backend/:

cd backend
npm install

# Create the database; copy the printed database_id into wrangler.toml.
wrangler d1 create totlprovision

# Apply the schema to the remote DB.
npm run db:init:remote

Edit backend/wrangler.toml:

  • set account_id,
  • paste the database_id from the create step,
  • set ACCESS_TEAM_DOMAIN = "totlcom.cloudflareaccess.com" (your Zero Trust team domain),
  • leave ACCESS_AUD blank for now — you fill it in step 2.

Deploy:

npm run deploy

Note the Worker URL it prints (e.g. https://totlprovision-api.<sub>.workers.dev) — the portal and engine point at it.


2. Entra SSO + Cloudflare Access

This is what protects the portal and lets the backend verify engineer identity.

Five parts, in order: (A) give the Worker a domain, (B) register an app in Microsoft Entra, (C) connect Entra to Cloudflare, (D) create the Access application, (E) copy the AUD to the Worker.

A. Give the Worker a custom domain

A workers.dev host can't be protected by Access, so the API needs a domain on your zone.

  1. Cloudflare dashboard → Workers & Pages → click the totlprovision-api worker.
  2. Settings tab → Domains & RoutesAddCustom Domain.
  3. Enter totlprovision-api.totlcom.comAdd domain. Wait until it shows Active (~1 min).

B. Register an app in Microsoft Entra

  1. Go to https://portal.azure.com → search Microsoft Entra ID → open it.
  2. Left menu → App registrationsNew registration.
  3. Name: Cloudflare Access. Supported account types: "Accounts in this organizational directory only". Redirect URI: platform Web, value: https://totlcom.cloudflareaccess.com/cdn-cgi/access/callback
  4. Register.
  5. On the app's Overview, copy Application (client) ID and Directory (tenant) ID (save both).
  6. Left menu → Certificates & secretsNew client secret → description + expiry → Add. Copy the secret Value immediately (it's hidden after you leave the page).
  7. Left menu → API permissionsAdd a permissionMicrosoft GraphDelegated → add openid, email, profile (and User.Read). → Add permissionsGrant admin consent.

C. Connect Entra to Cloudflare Zero Trust

  1. Go to https://one.dash.cloudflare.com (Zero Trust) → SettingsAuthentication.
  2. Under Login methodsAdd newAzure AD (Microsoft Entra ID).
  3. Paste App ID (client ID), Client secret (the Value), and Directory (tenant) ID.
  4. Save, then click Test on the method and sign in to confirm it works.

D. Create the Access application (covers BOTH hostnames)

  1. Zero Trust → AccessApplicationsAdd an applicationSelf-hosted.
  2. Application name: TotlProvision.
  3. Under Application domain, add the portal hostname totlprovision.totlcom.com, then click Add hostname and add totlprovision-api.totlcom.com. Both must be on this one application — that's what lets the login cookie work across the two (wildcards won't).
  4. Identity providers: select your Entra method (turn off One-time PIN if you only want SSO). Next.
  5. Add a policy: Name Allow Totlcom, Action Allow, Include → Emails ending in@totlcom.com (or pick a specific Entra group/emails). Next.
  6. CORS settings (in the app's settings): set Access-Control-Allow-Origins to https://totlprovision.totlcom.com, allow methods GET, POST, OPTIONS, and turn on Allow credentials. Save the application.

E. Copy the AUD to the Worker

  1. Zero Trust → Access → Applications → open TotlProvision → copy the Application Audience (AUD) Tag (a long hex string).
  2. Set it on the backend:
    cd backend
    wrangler secret put ACCESS_AUD     # paste the AUD tag when prompted
    wrangler deploy
    

Done. The backend now verifies each engineer's Cf-Access-Jwt-Assertion against your team JWKS and the AUD — no OAuth code to maintain.


3. Portal — Cloudflare Pages

Point the portal at your Worker, then deploy and assign its domain.

  1. The API origin is set in portal/index.html to the API custom domain:
    <script>window.TOTL_API = "https://totlprovision-api.totlcom.com";</script>
    
  2. Deploy from the repo root (not from backend/ or portal/, or the path resolves wrong):
    wrangler pages deploy portal --project-name totlprovision-portal
    
  3. Assign the custom domain totlprovision.totlcom.com to the Pages project (Pages → the project → Custom domains). It's protected by the combined Access application from step 2.

First run inside the portal: an admin clicks "First-time setup", enters a strong passphrase, and a tenant keypair is generated in the browser. Record that passphrase in your break-glass scheme — it cannot be recovered from the server (see Security).


4. Docs site — Cloudflare Pages (auto-deploy on push)

A separate Pages project, connected to Git so it rebuilds on every push. Full steps in Deploying these docs. Short version:

  • Workers & Pages → Create → Pages → Connect to Git → this repo.
  • Build command: pip install -r requirements.txt && mkdocs build
  • Output directory: site · Root directory: / · env PYTHON_VERSION=3.12.
  • Protect the hostname with Access (these docs contain internal architecture).

5. Issue a machine API token (per tenant)

The engine authenticates ingest with a per-tenant Bearer token. Only a salted hash is stored, so a DB leak can't replay it. Generate a token, insert its hash, and put the plaintext in the engine config (kept off source/USB):

# Example token + hash (Node). prefix = first 8 chars.
node -e '
const c=require("crypto");
const token="tok_"+c.randomBytes(24).toString("base64url");
const salt=c.randomBytes(8).toString("hex");
const hash=c.createHash("sha256").update(salt+":"+token).digest("hex");
console.log({token, prefix:token.slice(0,8), salt, hash});
'

Then insert the row (replace values):

cd backend
wrangler d1 execute totlprovision --remote --command \
"INSERT INTO tenants (id,name) VALUES ('11111111-1111-1111-1111-111111111111','Acme MSP');
 INSERT INTO users (id,tenant_id,email,role) VALUES ('u1','11111111-1111-1111-1111-111111111111','you@yourmsp.com','admin');
 INSERT INTO api_tokens (id,tenant_id,token_prefix,token_hash,token_salt,label)
 VALUES ('t1','11111111-1111-1111-1111-111111111111','<prefix>','<hash>','<salt>','engine');"

Hand the plaintext token to the engine (it will use it for /v1/report and /v1/secret). A GUI helper for tenant/token creation lands in a later phase.


Verify it's wired

# Worker is up
curl https://totlprovision-api.totlcom-inc.workers.dev/health      # -> {"ok":true,...}

# Token works (after step 5) — should 409 until a tenant key exists, which is expected
curl -H "Authorization: Bearer <plaintext-token>" \
  https://totlprovision-api.totlcom-inc.workers.dev/v1/tenant/pubkey

Then sign in to the portal (Access prompts Entra SSO), run admin onboarding to create the tenant key, and the /v1/tenant/pubkey call above will start returning the public key the engine encrypts to.