Skip to main content

Cloudflare Access Authentication Evolution

Status: Implemented

Owner: Symphony Core Systems Team

1. Problem

The current Cloudflare Access setup for team.symphonycore.com has three gaps:

  • No branding. The OTP login page uses Cloudflare's default appearance with no Symphony Core branding, making it look generic and potentially confusing for users encountering it for the first time.
  • Domain-locked access. Only @symphonycore.com email addresses can authenticate. There is no mechanism to grant access to specific external collaborators (contractors, clients, partners) without changing the domain policy.
  • No automated validation. There is no way to verify that the authentication configuration, site content, and deployment pipeline are working correctly without manually visiting the site and the Cloudflare dashboard. Changes to Access policies or deployments can silently break access.

2. Goal

Evolve the Cloudflare Access configuration to support branded login, individual external user access, and an automated end-to-end validation script -- all within the free tier (50 users).

3. Success Criteria

CriteriaMeasure
Login page shows Symphony Core brandingLogo, organization name, and brand colors visible on OTP page
External users can authenticateIndividual email addresses added to Access policy receive OTP and can log in
Validation is self-servicenpm run validate-access checks auth enforcement, content, and deploy health in one pass
No cost increaseAll changes stay within Cloudflare Zero Trust free tier (50 users)
ASCII-safe outputValidation script uses no Unicode (Windows cp1252 compatibility)

4. Functional Requirements

4.1 Login Page Branding (Cloudflare Dashboard)

  • FR-1: Set organization name to "Symphony Core" on the Access login page.
  • FR-2: Upload Symphony Core logo to the login page configuration.
  • FR-3: Set background color to brand color.
  • FR-4: Add header text: "Symphony Core Internal Documentation".
  • FR-5: Add footer text with support contact or instructions.

Configuration path: Zero Trust Dashboard > Reusable components > Custom pages > Access login page > Manage.

4.2 External User Access (Cloudflare Dashboard)

  • FR-6: Add an "Emails" Include rule to the symphonycore-docs Access application policy, alongside the existing "Emails ending in @symphonycore.com" rule.
  • FR-7: Individual email addresses added to this rule receive OTP and can authenticate like domain users.
  • FR-8: Document the process for adding/removing external users in the project README or runbook.
  • FR-9: Maintain a list of authorized external users in project documentation for audit purposes.

Configuration path: Zero Trust Dashboard > Access > Applications > symphonycore-docs > Edit > Policy rules.

4.3 End-to-End Validation Script

  • FR-10: Validate auth enforcement: confirm that unauthenticated requests to team.symphonycore.com receive a redirect (HTTP 302/303) to the Cloudflare Access login page, not a 200.
  • FR-11: Validate login page branding: confirm the login page response contains expected branding elements (organization name in HTML).
  • FR-12: Validate site content: confirm the site serves a 200 response with expected content markers (site title, Docusaurus markers) when accessed with a valid CF Access service token.
  • FR-13: Validate deployment health: query the Cloudflare Pages API for latest production deployment status (reuse deploy-status logic).
  • FR-14: Print a summary with clear pass/fail indicators for each check.
  • FR-15: Exit code 0 if all checks pass, 1 if any check fails.
  • FR-16: Support --verbose flag for detailed output on each check.

4.3.1 NPM Script Integration

  • FR-17: Expose as npm run validate-access.

4.4 Service Token for Automated Content Validation

  • FR-18: Create a Cloudflare Access service token for script-based content validation (bypasses OTP).
  • FR-19: Store service token credentials (CF_ACCESS_CLIENT_ID, CF_ACCESS_CLIENT_SECRET) in .env.local.
  • FR-20: Document service token creation steps in the script's error output (same pattern as deploy-status.js).

Configuration path: Zero Trust Dashboard > Access > Service Auth > Service Tokens > Create.

5. Non-Functional Requirements

  • NFR-1: Zero external dependencies beyond Node.js stdlib and dotenv (already in the project).
  • NFR-2: ASCII-safe output only -- no emoji or Unicode symbols (Windows cp1252 compatibility).
  • NFR-3: All credentials stored in .env.local (gitignored), never committed.
  • NFR-4: Guided self-service setup: error messages include exact steps to create tokens and configure policies.
  • NFR-5: Free tier only -- no changes that require Cloudflare Zero Trust paid plan.

6. Configuration

Existing (no changes)

VariableSourcePurpose
R2_ACCOUNT_ID.env.localCloudflare account ID
CLOUDFLARE_API_TOKEN.env.localAPI token with Pages:Read permission
CLOUDFLARE_PAGES_PROJECT.env.localPages project name

New

VariableSourcePurpose
CF_ACCESS_CLIENT_ID.env.localService token Client ID for content validation
CF_ACCESS_CLIENT_SECRET.env.localService token Client Secret for content validation
SITE_URL.env.localSite URL to validate (default: https://team.symphonycore.com)

7. Architecture

npm run validate-access
|
v
scripts/validate-access.js
|
|--> GET {SITE_URL} (no auth headers)
| Expect 302/303 redirect to CF Access login
|
|--> GET {redirect URL}
| Check for branding elements in login page HTML
|
|--> GET {SITE_URL} (with CF-Access-Client-Id + CF-Access-Client-Secret headers)
| Expect 200 with site content
|
|--> GET Cloudflare Pages API /deployments
| Check latest production deployment status
|
v
Summary output + exit code

8. Constraints

  • 50-user cap: Cloudflare Zero Trust free tier supports up to 50 authenticated users. Adding external users counts toward this limit. Monitor usage in Zero Trust Dashboard > Users.
  • No custom HTML on login page: The login page customization is template-based (logo, colors, text fields only). No custom HTML/CSS injection is possible on any plan.
  • Service tokens bypass IdP: The service token used for content validation bypasses OTP. It should have minimal scope and be rotated periodically.

9. Files

FileRole
scripts/validate-access.jsE2E validation script (new)
package.jsonNPM script entry validate-access (modified)
.env.exampleDocuments new environment variables (modified)
.env.localActual credentials (gitignored)
docs/prd/prd-auth-evolution.mdThis PRD

10. External User Registry

The following external users have been granted access. Update this list when adding or removing users.

EmailGrantedPurpose
symphonycorehq@gmail.com2026-03-04External team access

To add a user: Zero Trust Dashboard > Access > Applications > symphonycore-docs > Edit policy > Add email to "Emails" Include rule.