# Cloudflare Access (Zero Trust) Setup

## Overview
Cloudflare Access sits in front of subdomains and checks user identity before traffic reaches the origin server. Provides SSO and 2FA for any self-hosted service without server-side auth code.

## When to Use
- Replace Caddy basic_auth for internal pages
- Add 2FA layer over existing login systems (TwentyCRM magic link, DocuSeal)
- Unify auth across multiple services (CRM, internal dashboard, DocuSeal)
- Protect any self-hosted web app without modifying the app itself

## Prerequisites
- Cloudflare API token with **Access: Apps and Policies: Edit** permission
- Existing DNS-only token can be updated (Edit → Add permission, then regenerate)
- Subdomains must be proxied through Cloudflare (orange cloud) for Access to work

## Setup via API

### Account
- Account ID: from `GET /client/v4/accounts`
- Token: with Access: Apps and Policies: Edit permission

### Create an Access Application
```bash
curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCT/access/apps" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"App Name","domain":"subdomain.yourdomain.com","type":"self_hosted","session_duration":"24h"}'
```

### Add a Policy to an Application
```bash
APP_ID=$(curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCT/access/apps" ... | jq '.result[] | select(.name=="App Name").id')

# Policy with email domain include (no 2FA requirement yet)
curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCT/access/apps/$APP_ID/policies" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Team","decision":"allow","include":[{"email_domain":{"domain":"germainebrown.com"}}]}'
```

### Policy Types
- `include` — who is ALLOWED (email, email_domain, everyone, etc.)
- `require` — what is REQUIRED (totp, mfa, etc.)
- `exclude` — who is BLOCKED

### Known Issues
- `require: [{totp: {}}]` returned `invalid 'require' configuration` in testing (Jul 7, 2026). May need to configure 2FA at the Zero Trust org level first, or use the dashboard UI.
- 2FA/one-time passcode is a "require" rule but may need to be set via the Cloudflare dashboard rather than API for initial setup.
- Session duration of "24h" means users re-authenticate daily. Adjust based on security needs.

## Dashboard Setup (Fallback)
If API fails:
1. Login at https://one.dash.cloudflare.com/
2. Access → Applications → Add application → Self-hosted
3. Enter subdomain, session duration
4. Add policy: name, include (email or email_domain), require (2FA if needed)
5. Save

## DNS Requirement
- Subdomains covered by Access must be proxied (orange cloud) in Cloudflare DNS
- This enables Cloudflare to intercept requests before they reach the origin
- Non-proxied (gray cloud) records bypass Access entirely

## Removing Caddy Basic Auth After Access is Working
Once Access is confirmed working, remove the `basic_auth` block from the Caddyfile or the old auth section:
```bash
# In Caddyfile: remove @internal handle block and basic_auth
# Reload: caddy reload --config /etc/caddy/Caddyfile
```
