Overview
The voting system uses a flexible authentication system that supports multiple authentication methods with a clear priority order. This allows for secure production deployments while maintaining ease of development.
Authentication Methods
The system supports three authentication methods, checked in priority order:
- Cloudflare Access (Zero Trust) - Highest priority (production)
- ADMIN_API_KEY - Fallback for environments without Cloudflare Access
- Local Development Bypass - Automatic bypass for local development
Priority Order
Cloudflare Access → ADMIN_API_KEY → Local Dev Bypass → 401 Unauthorized
Cloudflare Access (Zero Trust)
Priority: Highest (checked first)
Cloudflare Access provides enterprise-grade authentication without requiring API keys. When configured, it automatically authenticates users through Cloudflare’s Zero Trust network.
How It Works
Cloudflare Access sets HTTP headers when a user is authenticated:
CF-Access-JWT-Assertion: JWT token from CloudflareCF-Access-Authenticated-User-Email: Email of authenticated user
The system checks for either header to determine authentication status.
Setup
-
Configure Cloudflare Access:
- Go to Cloudflare Dashboard → Zero Trust → Access
- Create an Access Application
- Add your Worker route (e.g.,
/admin/*) - Configure identity providers (Google, GitHub, email, etc.)
- Set up policies for who can access
-
No Code Changes Required:
- Once configured, Cloudflare automatically injects headers
- The system automatically detects and uses these headers
Benefits
- No API keys to manage
- Enterprise SSO support
- Automatic user identification
- Built-in MFA support
- Audit logging via Cloudflare
When to Use
- Production deployments
- Multi-user environments
- When you need user identification
- Enterprise/organizational deployments
ADMIN_API_KEY Authentication
Priority: Second (fallback if Cloudflare Access not available)
A simple API key-based authentication for environments where Cloudflare Access is not configured.
How It Works
- Set
ADMIN_API_KEYas a Cloudflare secret - Include the key in the
Authorizationheader - System validates the key matches
Header Format
The system supports two formats:
Authorization: Bearer YOUR_API_KEYor
Authorization: YOUR_API_KEYSetup
Via Wrangler CLI:
wrangler secret put ADMIN_API_KEYVia Cloudflare Dashboard:
- Go to Workers & Pages → Your Worker
- Settings → Variables and Secrets
- Add secret:
ADMIN_API_KEY - Enter your API key value
For Local Development:
Add to .dev.vars:
ADMIN_API_KEY=your-secret-key-here
Usage Example
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://your-worker.workers.dev/admin/electionsSecurity Considerations
- Use Strong Keys: Generate a long, random API key
- Rotate Regularly: Change keys periodically
- Keep Secret: Never commit keys to version control
- HTTPS Only: Always use HTTPS in production
When to Use
- Single-user deployments
- Development/staging environments
- When Cloudflare Access is not available
- API integrations
Local Development Bypass
Priority: Third (only in local development)
For local development, authentication is automatically bypassed when running on:
localhost127.0.0.1*.dev(Wrangler dev tunnel)
This allows developers to test without configuring authentication.
How It Works
The system checks the request hostname:
hostname === 'localhost' ||
hostname === '127.0.0.1' ||
hostname.endsWith('.dev')If any condition is true, authentication is bypassed.
Security
⚠️ IMPORTANT: This bypass only works in local development. In production (Cloudflare Workers), this bypass is automatically disabled because:
- Production URLs don’t match these patterns
- Cloudflare Workers run in a different environment
When to Use
- Local development only
- Testing without authentication setup
- Development workflows
Implementation Details
Middleware
Authentication is enforced via the requireAdmin middleware in src/middleware/auth.ts:
export async function requireAdmin(
c: Context<{ Bindings: CloudflareBindings }>,
next: Next
)Applied Routes
The middleware is applied to:
- All
/admin/*routes (API endpoints) - All
/adminroutes (UI pages)
Response Format
When authentication fails, the system returns:
{
"error": "Authentication required",
"message": "Use Cloudflare Access (Zero Trust) or provide a valid ADMIN_API_KEY in the Authorization header"
}Status code: 401 Unauthorized
Admin UI Authentication
The admin UI (/admin/* pages) uses the same authentication system but also:
- Passes the API key to the frontend (if using API key auth)
- Allows frontend JavaScript to make authenticated API calls
- Hides the API key when using Cloudflare Access
Frontend API Key Handling
When using API key authentication (not Cloudflare Access), the admin UI:
- Receives the API key from the backend
- Stores it in memory (not localStorage)
- Includes it in API requests via
Authorizationheader - Never exposes it in URLs or logs
Best Practices
Production
- Use Cloudflare Access for production deployments
- Don’t set ADMIN_API_KEY if using Cloudflare Access
- Monitor access logs via Cloudflare dashboard
- Set up MFA in Cloudflare Access policies
Development
- Use ADMIN_API_KEY for local development
- Add to
.dev.vars(gitignored) - Never commit secrets to version control
- Use different keys for different environments
Security
- Rotate keys regularly
- Use strong, random keys (32+ characters)
- Limit access to necessary personnel
- Monitor for unauthorized access
- Use HTTPS in all environments
Troubleshooting
”Authentication required” Error
Possible causes:
- Cloudflare Access not configured and no ADMIN_API_KEY set
- API key incorrect or not included in request
- Request not from localhost (in development)
Solutions:
- Configure Cloudflare Access OR set ADMIN_API_KEY
- Verify API key is correct and in Authorization header
- Check request is coming from localhost/127.0.0.1/*.dev
Cloudflare Access Not Working
Check:
- Access application is configured correctly
- User is in allowed groups/policies
- Route matches Access application path
- Headers are being set (check in browser DevTools)
API Key Not Working
Check:
- Key is set correctly in secrets
- Key matches exactly (no extra spaces)
- Header format is correct (
Bearer TOKENorTOKEN) - Worker has been redeployed after setting secret
See Also
- ops - General infrastructure documentation
- rate-limiting - Rate limit configurations
- api-reference - API endpoint documentation