Authentication Configuration Guide
Authentication Configuration Guide
Complete reference for all environment variables and configuration needed to run the bloqr-backend authentication system with Better Auth.
Environment Variables
Better Auth
Required
| Variable | Required | Source | Description |
|---|---|---|---|
BETTER_AUTH_SECRET | Yes | openssl rand -base64 32 | HMAC signing key for session tokens. Must be ≥ 32 characters. Never reuse across environments. Changing this invalidates all active sessions. |
BETTER_AUTH_URL | Yes | Your Worker’s public URL | Base URL for OAuth callbacks and email links. Example: https://your-worker.workers.dev |
Optional
| Variable | Required | Source | Description |
|---|---|---|---|
BETTER_AUTH_API_KEY | Optional | dash.better-auth.com | API key for Better Auth Dash dashboard integration. Enables dash() and sentinel() connectivity. Must be passed explicitly via apiKey: env.BETTER_AUTH_API_KEY — Cloudflare Workers do not expose Worker Secrets via process.env. Production: wrangler secret put BETTER_AUTH_API_KEY |
BETTER_AUTH_KV_URL | Optional | Cloudflare KV REST API URL | REST API URL for the BETTER_AUTH_KV namespace. Used by dash() and sentinel() kvUrl option. Construct from: https://api.cloudflare.com/client/v4/accounts/<CF_ACCOUNT_ID>/storage/kv/namespaces/<BETTER_AUTH_KV_ID>. Production: wrangler secret put BETTER_AUTH_KV_URL |
Database (Required)
| Variable | Required | Source | Description |
|---|---|---|---|
HYPERDRIVE | Yes | wrangler.toml Hyperdrive binding | Cloudflare Hyperdrive binding that proxies connections to Neon PostgreSQL. The Worker connects via env.HYPERDRIVE.connectionString. |
DIRECT_DATABASE_URL | Dev/CI only | Neon connection string | Direct PostgreSQL URL for Prisma CLI migrations (prisma migrate dev/deploy). Never read by the Worker at runtime. |
Social OAuth Providers (Optional)
| Variable | Required | Source | Description |
|---|---|---|---|
GITHUB_CLIENT_ID | Optional | GitHub → Settings → Developer Settings → OAuth Apps | Client ID for GitHub sign-in. GitHub is enabled only when both GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET are set. |
GITHUB_CLIENT_SECRET | Optional | GitHub OAuth App settings | Client secret for GitHub sign-in. |
GOOGLE_CLIENT_ID | Optional | Google Cloud Console → Credentials | Client ID for Google sign-in (reserved — enable by uncommenting in auth.ts). |
GOOGLE_CLIENT_SECRET | Optional | Google Cloud Console → Credentials | Client secret for Google sign-in. |
Cloudflare Turnstile (Optional — Bot Protection)
| Variable | Required | Source | Description |
|---|---|---|---|
TURNSTILE_SITE_KEY | Optional | Cloudflare Dashboard → Turnstile | Public site key for the frontend widget. Returned via GET /api/turnstile-config. |
TURNSTILE_SECRET_KEY | Optional | Cloudflare Dashboard → Turnstile | Server-side verification key. Presence of this var gates the Better Auth captcha() plugin (no separate enabled flag) — see Plugin Extension Pack → captcha(). Disabled when not set. |
Better Auth Plugin Extension Pack (Conditional)
All optional. Every conditionally-loaded plugin defaults to off — see the Plugin Extension Pack guide for what each plugin does, its endpoints, and rollout gotchas before setting any of these in production.
| Variable | Required | Gates | Description |
|---|---|---|---|
BETTER_AUTH_APIKEY_ENABLED | Optional | apiKey() (@better-auth/api-key) | Set to "true" to mount the official API key plugin — a system deliberately kept separate from this project’s custom ApiKey/blq_-prefixed implementation. See the plugin guide before enabling. |
BETTER_AUTH_MCP_ENABLED | Optional | mcp() | Set to "true" to mount the OAuth provider for MCP clients. Mutually exclusive with BETTER_AUTH_OIDC_ENABLED — enabling both throws a WorkerConfigurationError at startup. |
BETTER_AUTH_OIDC_ENABLED | Optional | oidcProvider() | Set to "true" to make bloqr an OIDC provider for third-party/enterprise clients. Mutually exclusive with BETTER_AUTH_MCP_ENABLED. |
BETTER_AUTH_OIDC_ALLOW_DYNAMIC_CLIENT_REGISTRATION | Optional | oidcProvider() | Set to "true" to open the /register endpoint to self-service OAuth client registration. Only meaningful when BETTER_AUTH_OIDC_ENABLED=true. Defaults to disabled (trusted/manually-registered clients only). |
BETTER_AUTH_SSO_ENABLED | Optional | sso() (@better-auth/sso) | Set to "true" to mount enterprise SAML/OIDC login. Safe to enable with zero identity providers registered. |
BETTER_AUTH_SCIM_ENABLED | Optional | scim() (@better-auth/scim) | Set to "true" to mount inbound SCIM user provisioning. |
BETTER_AUTH_PASSKEY_ENABLED | Optional | passkey() (@better-auth/passkey) | Set to "true" to mount WebAuthn. Requires BETTER_AUTH_PASSKEY_RP_ID to also be set. |
BETTER_AUTH_PASSKEY_RP_ID | Required if passkey enabled | passkey() | WebAuthn Relying Party ID — the auth server’s bare effective domain (e.g. bloqr.dev, api.bloqr.dev; localhost valid for local dev). No scheme, port, or path. Zod-validated at startup; a malformed value throws rather than silently breaking WebAuthn in the browser. |
BETTER_AUTH_PASSKEY_RP_NAME | Optional | passkey() | Human-readable name shown in the OS passkey prompt. Defaults to "Bloqr". |
Every flag except TURNSTILE_SECRET_KEY and BETTER_AUTH_PASSKEY_RP_ID/_RP_NAME accepts
only the literal strings "true" or "false" — any other value (e.g. "TRUE", "1")
throws an AuthPluginConfigError at startup rather than silently behaving as disabled.
Cloudflare Access (Optional — Defense-in-Depth)
| Variable | Required | Source | Description |
|---|---|---|---|
CF_ACCESS_TEAM_DOMAIN | Optional | Cloudflare Zero Trust Dashboard | Your Access team domain (e.g., mycompany). CF Access checks are skipped when not set. |
CF_ACCESS_AUD | Optional | CF Access → Applications → AUD Tag | Audience claim for CF Access JWT verification. Required if CF_ACCESS_TEAM_DOMAIN is set. |
Other Runtime Variables
| Variable | Required | Source | Description |
|---|---|---|---|
CORS_ALLOWED_ORIGINS | Optional | Comma-separated URLs | Allowed CORS origins. Example: https://app.example.com,https://admin.example.com |
ENVIRONMENT | Optional | wrangler.toml or .dev.vars | development, staging, or production. Used for log level and error detail. |
Local Development Setup
Quick Start
# 1. Create a personal dev branch in the Neon Console (one-time)# https://console.neon.tech → bloqr-backend project → Branches → New Branch
# 2. Copy the example filecp .dev.vars.example .dev.vars
# 3. Fill in your Neon branch connection string (see template below)# 4. Start the Workerwrangler dev.dev.vars Template
# .dev.vars — NOT committed to git# Loaded by wrangler dev. Overrides values in wrangler.toml [vars].
ENVIRONMENT=development
# ─── Better Auth ──────────────────────────────────────────────────────────────# Generate: openssl rand -base64 32BETTER_AUTH_SECRET=replace-with-openssl-rand-base64-32-outputBETTER_AUTH_URL=http://localhost:8787
# ─── Database ─────────────────────────────────────────────────────────────────# Point wrangler dev at your personal Neon development branch.# Create a branch at https://console.neon.tech → your project → Branches → New Branch.# Use the "Direct connection" string (not pooled). See: https://neon.com/guides/local-development-with-neonCLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE=postgresql://<user>:<password>@<branch-host>.neon.tech/<dbname>?sslmode=require
# ─── Social OAuth (optional) ──────────────────────────────────────────────────# GITHUB_CLIENT_ID=your-github-client-id# GITHUB_CLIENT_SECRET=your-github-client-secret# GOOGLE_CLIENT_ID=your-google-client-id # reserved# GOOGLE_CLIENT_SECRET=your-google-client-secret
# ─── Turnstile (use Cloudflare test keys locally) ─────────────────────────────TURNSTILE_SITE_KEY=1x00000000000000000000AATURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA
# ─── CORS ─────────────────────────────────────────────────────────────────────CORS_ALLOWED_ORIGINS=http://localhost:4200,http://localhost:8787Local Database
The project uses Neon branching for local development — no Docker PostgreSQL needed. Each developer has a personal, isolated Neon branch that can be reset or deleted freely.
# Apply pending migrations to your Neon branchdeno task db:migrate
# Generate Prisma clientdeno task db:generate
# Start the Worker (uses .dev.vars for DB connection)wrangler devSee Local Development Setup for full Neon branching instructions.
Wrangler Bindings (wrangler.toml)
The Worker’s database connection uses a Cloudflare Hyperdrive binding — not a plain
connection string. The binding is declared in wrangler.toml:
[[hyperdrive]]binding = "HYPERDRIVE"id = "800f7e2edc86488ab24e8621982e9ad7"During wrangler dev, add this to .dev.vars to override with your Neon dev branch:
# Use your personal Neon dev branch (direct connection, not pooled)CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE=postgresql://<user>:<password>@<branch-host>.neon.tech/<dbname>?sslmode=requireKV Namespaces
| Binding | Purpose |
|---|---|
COMPILATION_CACHE | Compiled adblock filter list cache |
RATE_LIMIT | Rate-limit counters |
METRICS | Worker metrics |
BETTER_AUTH_KV | Better Auth secondary storage — sessions, rate-limit counters, verification tokens. Create: wrangler kv:namespace create BETTER_AUTH_KV |
Feature flags are managed via Cloudflare Flagship (the
[[flagship]]binding), not a KV namespace.
For full Hyperdrive setup, see Better Auth Prisma Setup.
Production Deployment
Non-Secrets in wrangler.toml [vars]
Static, non-sensitive values that are safe to commit:
[vars]COMPILER_VERSION = "0.62.5"ENVIRONMENT = "production"BETTER_AUTH_URL = "https://your-worker.workers.dev"TURNSTILE_SITE_KEY = "0x4AAA..." # public site key — not a secretSecrets via wrangler secret put
Never commit secrets to wrangler.toml or any .env.* file:
# Better Auth — requiredwrangler secret put BETTER_AUTH_SECRET
# Better Auth Dash / sentinel — optional (auditLogs pending upstream)wrangler secret put BETTER_AUTH_API_KEYwrangler secret put BETTER_AUTH_KV_URL
# Social OAuth — if using GitHubwrangler secret put GITHUB_CLIENT_IDwrangler secret put GITHUB_CLIENT_SECRET
# Social OAuth — if using Googlewrangler secret put GOOGLE_CLIENT_IDwrangler secret put GOOGLE_CLIENT_SECRET
# Turnstilewrangler secret put TURNSTILE_SECRET_KEY
# CORSwrangler secret put CORS_ALLOWED_ORIGINS
# CF Access (optional)wrangler secret put CF_ACCESS_TEAM_DOMAINwrangler secret put CF_ACCESS_AUDDatabase Schema
Better Auth uses Neon PostgreSQL (accessed via Cloudflare Hyperdrive) with Prisma as the ORM.
The schema is in prisma/schema.prisma.
Better Auth Core Tables
| Table | Contents |
|---|---|
user | User accounts — id, name, email, tier, role, banned, emailVerified |
session | Active sessions — id, userId, token (hashed), expiresAt, ipAddress, userAgent |
account | OAuth provider links — userId, providerId, providerAccountId, accessToken |
verification | Email verification and password-reset tokens |
twoFactor | TOTP secrets per user (added by twoFactor() plugin) |
Custom User Fields
Two custom fields are added to the user table via additionalFields in auth.ts:
| Field | Type | Default | input | Description |
|---|---|---|---|---|
tier | string | free | false | Determines rate limits and feature access. Not user-settable. |
role | string | user | false | user or admin. Not user-settable. |
input: false means the field is excluded from sign-up/sign-in body parsing — only server-side
code (migrations, admin endpoints) can write these fields.
Running Migrations
# Development — creates migration file + applies to local DBdeno task db:migrate
# Production / CI — applies pending migrations without promptingdeno task db:migrate:deploy
# Regenerate Prisma client after schema changesdeno task db:generateAuthentication Endpoints
All auth routes are under /api/auth/* (configured by basePath: '/api/auth' in auth.ts).
| Endpoint | Method | Description |
|---|---|---|
/api/auth/sign-up/email | POST | Register with email + password |
/api/auth/sign-in/email | POST | Sign in with email + password |
/api/auth/sign-out | POST | Sign out (clears session) |
/api/auth/sign-in/social?provider=github | GET | Initiate GitHub OAuth flow |
/api/auth/callback/github | GET | GitHub OAuth callback (automatic) |
/api/auth/forget-password | POST | Request password-reset email |
/api/auth/reset-password | POST | Submit new password with reset token |
/api/auth/two-factor/enable | POST | Enable TOTP 2FA |
/api/auth/two-factor/verify | POST | Verify TOTP code |
/api/auth/two-factor/disable | POST | Disable TOTP 2FA |
/api/auth/list-sessions | GET | List all active sessions (auth required) |
/api/auth/revoke-session | POST | Revoke a session by ID |
/api/auth/revoke-other-sessions | POST | Revoke all sessions except current |
/api/auth/admin/list-users | GET | Admin: list all users |
/api/auth/admin/set-role | POST | Admin: change user role |
/api/auth/admin/ban-user | POST | Admin: ban a user |
/api/auth/admin/unban-user | POST | Admin: unban a user |
/api/auth/admin/revoke-user-sessions | POST | Admin: revoke all sessions for a user |
/api/auth/providers | GET | List active providers (emailPassword, github, mfa) |
Session Configuration
| Setting | Value | Location |
|---|---|---|
| Session TTL | 7 days | expiresIn: 60 * 60 * 24 * 7 in auth.ts |
| Auto-refresh | Within 1 day of expiry | updateAge: 60 * 60 * 24 |
| Cookie cache | 5 minutes | cookieCache: { enabled: true, maxAge: 5 * 60 } |
| Cookie prefix | adblock | cookiePrefix: 'adblock' |
| Cookie name | adblock.session_token | Derived from prefix |
Deployment Checklist
First-Time Setup
- Provision Neon PostgreSQL database
- Create Cloudflare Hyperdrive configuration pointing to Neon
- Generate
BETTER_AUTH_SECRET:openssl rand -base64 32 - Set
BETTER_AUTH_SECRETandBETTER_AUTH_URLin.dev.vars(local) or viawrangler secret put(production) - Run migrations:
deno task db:migrate - Deploy Worker:
wrangler deploy - Create first admin user (via Prisma Studio — set
role = admin,tier = admin) - Verify auth flow end-to-end
- (Optional) Enable Better Auth Dash: set
BETTER_AUTH_API_KEYviawrangler secret put BETTER_AUTH_API_KEY— the key is passed explicitly todash()andsentinel()viaapiKey: env.BETTER_AUTH_API_KEY; there is no automaticprocess.envpickup in Cloudflare Workers
Adding GitHub OAuth
- Create GitHub OAuth App (callback:
https://your-worker.workers.dev/api/auth/callback/github) -
wrangler secret put GITHUB_CLIENT_ID -
wrangler secret put GITHUB_CLIENT_SECRET -
wrangler deploy - Verify:
curl https://your-worker.workers.dev/api/auth/providers
Related Documentation
- Better Auth Developer Guide — Plugin configuration
- Better Auth Prisma Setup — Hyperdrive + Prisma adapter
- Social Providers — GitHub/Google OAuth setup
- Better Auth Admin Guide — Secret rotation, user management
Bloqr AI™ — The privacy you didn't know you needed.
© 2026 Bloqr AI™, a trademark of Bloqr Systems™. Created by Bloqr Systems™, founded by Jayson Knight.
Internet Hygiene (n.) — the ongoing practices that keep your digital life clean, private, and safe.
Our product repos live in the BloqrAI org, part of the Bloqr Systems GitHub Enterprise. Product repos are internal-visibility — enterprise membership is required to view them.