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. Log into your ⁠Cloudflare Zero Trust Dashboard. - https://one.dash.cloudflare.com
  2. In the left-hand sidebar, navigate to Integrations → Identity providers.
  3. Click the Add new identity provider button.
  4. Select Microsoft Entra ID (or Azure AD depending on your exact UI instance layout).
  5. Paste your Application (client) ID, Client secret Value, and Directory (tenant) ID.
  6. Scroll down and click Save. You can then click Test to verify the SSO loop

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 docs.totlprovision.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 Select ALL Methods, 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: bash 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: ```html

2. Deploy from the **repo root** (not from `backend/` or `portal/`, or the path resolves wrong):bash wrangler pages deploy portal --project-name totlprovision-portal `` 3. Assign the custom domaintotlprovision.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.