Internal — not for client sharing

Internal · Reference

Cloudflare Access & Auth

How our hosted docs, proposals, and client spaces are gated. Last updated Sep 25, 2026.

How it works

We use Cloudflare Access (part of Cloudflare Zero Trust) to gate our hosted docs and client spaces. When someone visits a gated subdomain, Cloudflare intercepts the request at the edge and shows a login screen before the page ever loads. No code changes needed per site.

The Zero Trust dashboard is at:

dash.cloudflare.com/.../access-controls/apps

Team domain: mangroveweb.cloudflareaccess.com

Login methods

Two identity providers are configured (Settings > Authentication > Login methods):

MethodWho uses itNotes
Google OAuth Mangrove team (@mangrove-web.com) Connected to a Google Cloud project ("Mangrove CF Access"). Redirect URI: https://mangroveweb.cloudflareaccess.com/cdn-cgi/access/callback
One-time PIN (email) Firefly, clients, anyone without Google Workspace Built into Cloudflare Access by default. User enters their email, gets a code, enters it. No setup needed per person.

Microsoft/Azure AD is not connected today — Firefly folks and clients use the email one-time PIN. To add either provider, follow the steps below.

Setting up a login method

All identity providers are added under Zero Trust > Settings > Authentication > Login methods > Add new. Every provider uses the same callback URL: https://mangroveweb.cloudflareaccess.com/cdn-cgi/access/callback.

Google (Workspace / OAuth)

  1. Google Cloud Console > the "Mangrove CF Access" project > APIs & Services > Credentials > Create credentials > OAuth client ID > Web application.
  2. Add the callback URL above as an Authorized redirect URI.
  3. Copy the Client ID and Client Secret.
  4. In Zero Trust, add a Google login method and paste them. Save, then Test.

Microsoft (Entra ID / Azure AD)

  1. Entra admin center > App registrations > New registration.
  2. Add the callback URL above as a Web redirect URI.
  3. Copy the Application (client) ID and Directory (tenant) ID.
  4. Certificates & secrets > New client secret > copy the secret value.
  5. API permissions > Microsoft Graph (delegated): openid, email, profile, User.Read > grant admin consent.
  6. In Zero Trust, add an Azure AD login method > paste the client ID, secret, and tenant ID. Save, then Test.

Once added, each Access application's policy can require a specific identity provider or allow all of them. External folks (Firefly, clients) can still use the email one-time PIN.

Access tiers

Each Access application has a policy that controls who gets in. We use two patterns:

TierPolicy ruleWho
Mangrove only Emails ending in @mangrove-web.com Internal docs, the hub index, tools
Mangrove + Firefly Emails ending in @mangrove-web.com + @fireflypartners.com Joint client spaces, shared docs

To gate something for a specific client, add their individual email addresses to the policy's include rules (not a domain rule, since clients don't share a domain).

Current Access applications

As of Sep 25, 2026:

Application nameDestinationWhat it isPolicy
docsdocs.mangrove-web.comInternal Mangrove Team index of all published Mangrove docs, proposals, and client linksMangrove Team
maiyadocsmaiyadocs.mangrove-web.comMaiya's dochubMaiya only
mgffmgff.mangrove-web.comFirefly x Mangrove shared workspace (estimates, resourcing, shared docs map)Mangrove + Firefly
madeforgoodmadeforgood.mangrove-web.comMade for Good venture docs (Maiya + Jen, data hub, ecosystem maps)Mangrove + Firefly
regardingherregardingher.mangrove-web.comRegarding Her Foods (RHF) client spaceMangrove + Firefly

Update this table when you add or remove applications.

Client subdomains: two-tier auth pattern

Each client subdomain should use two Access applications to separate team-only docs from client-shared docs:

Path patternPolicyWho sees it
subdomain.mangrove-web.com/*Mangrove + FireflyTeam only (everything by default: project plans, budgets, internal notes)
subdomain.mangrove-web.com/client/*Mangrove + Firefly + client contactsTeam + the client (deliverables, status, shared docs)
subdomain.madeforgood.ai/*Mangrove + FireflyTeam only — same pattern on the Made for Good domain
subdomain.madeforgood.ai/client/*Mangrove + Firefly + client contactsTeam + the client

The same two-tier pattern applies on both mangrove-web.com and madeforgood.ai subdomains — one Access app locking the whole subdomain to the team, a second opening /client/* to client contacts.

The more specific path wins. The base policy locks the entire subdomain to the team. Only /client/* opens up to client contacts. Everything outside /client/ stays team-only.

To add client contacts: edit the /client/* app's policy and add their individual emails as include rules. They log in via the email one-time PIN.

Domains left open (no gate)

DomainWhy it's open
proposals.mangrove-web.comClients access their proposals here. Gating it locks them out.
prod2026.mangrove-web.comPublic Mangrove website.
annual25.mangrove-web.comPublic Mangrove website.
claudetraining.mangrove-web.comTraining sandbox, shared openly.

Client .pages.dev domains

Client projects on *.pages.dev (e.g. kids-in-need-of-defense.pages.dev, rhf-artifacts.pages.dev) are not gated by Access. They rely on unguessable token paths for security.

To gate a .pages.dev domain, you have two options:

  1. Assign a custom subdomain under mangrove-web.com (e.g. kind.mangrove-web.com) and create an Access application for it. The custom domain must have its DNS record set to Proxied (orange cloud) for Access to intercept traffic.
  2. Create an individual Access application for the .pages.dev domain directly. This works but requires one app per project since you can't wildcard *.pages.dev (Cloudflare owns that domain, not you).

Adding a new Worker/Pages project

Client spaces, deliverables, internal docs, and the marketing site are all Cloudflare Pages projects. Two ways to deploy one:

MethodWhen to use it
Git-connect (default)Connect the GitHub repo in the Pages dashboard. Every push to the production branch builds and deploys automatically. Preferred for anything with an ongoing life.
Direct upload (wrangler pages deploy, or a repo's publish.sh)Move-fast fallback, or a project with no git connection. You deploy from your machine; nothing rebuilds on push.

Build output directory: published.

On a git-connected project, set Settings > Builds & deployments > Build output directory to published — not the repo root. Our builds write the finished site into published/. Leaving it at the default root deploys the site one level deep, so the clean URL keeps serving the old copy — the classic "I pushed but it's not updating."

A new project first comes up on <project>.pages.dev. To move it onto <name>.mangrove-web.com, add the custom domain under Pages > the project > Custom domains (its DNS record must be Proxied, the orange cloud). Then decide how it's gated: gate it with Access (see adding a new Access application below), or leave it open on an unguessable token path — the same trade-off as the client .pages.dev domains above.

How to add a new Access application

  1. Go to Zero Trust > Access controls > Applications
  2. Click + Create new application
  3. Select Self-hosted and private, click Continue
  4. Set the Application name (use the subdomain, e.g. "kind")
  5. Under Destinations, enter the full subdomain (e.g. kind.mangrove-web.com)
  6. Under Policies, create a policy:
    • Policy name: "Mangrove Team" or "Mangrove + Firefly"
    • Action: Allow
    • Include: Emails ending in @mangrove-web.com
    • For Firefly access: add a second include rule for @fireflypartners.com
    • For client access: add individual emails
  7. Session duration: 24 hours is a reasonable default
  8. Save

DNS requirement: the subdomain's DNS record must be Proxied (orange cloud icon in DNS settings), not DNS-only (gray cloud). Access cannot intercept traffic that doesn't flow through Cloudflare's proxy.

How to add a person to a policy

To give a specific person (a client contact, a contractor) access to a gated subdomain:

  1. Go to the Access application for that subdomain
  2. Edit the policy
  3. Add an include rule: Emails > enter their exact email address
  4. Save

They'll use the email one-time PIN to log in (no Google account needed).

Removing access

To remove a person: edit the policy and delete their email from the include rules.

To un-gate a subdomain entirely: delete the Access application. The site becomes open immediately.

To revoke an active session: go to Insights & Logs > Access > Active Users and revoke their session.

Middleware (legacy)

The doc hub (docs.mangrove-web.com) previously used HTTP Basic Auth via a Cloudflare Pages Function (platform/functions/_middleware.js) with a HUB_PASSWORD environment secret. That password gate was removed on Sep 25, 2026 when Cloudflare Access was set up.

The middleware still exists but only handles:

Troubleshooting

A subdomain isn't showing the Access login

Check that the DNS record is Proxied (orange cloud), not DNS-only. Go to the main Cloudflare dashboard > mangrove-web.com zone > DNS > Records, find the subdomain, and toggle proxy on.

Someone can't log in

Confirm their email is in the policy's include rules (exact match). If they're using Google and it fails, the email one-time PIN is the fallback. They just enter their email on the Access login screen.

Google OAuth stops working

The OAuth credentials live in a Google Cloud project ("Mangrove CF Access"). Check that the client secret hasn't expired and the redirect URI is still https://mangroveweb.cloudflareaccess.com/cdn-cgi/access/callback.

"Authentication required" browser popup (not the Access login page)

That's the old HTTP Basic Auth, not Cloudflare Access. It means the middleware still has the password gate for that route. Check platform/functions/_middleware.js and the HUB_PASSWORD Pages secret.