Cloudflare Adapter
The Cloudflare adapter provides Cloudflare-specific integrations for AuthHero, including custom domain management, caching, and optional logging solutions.
Features
- Custom Domains: Manage custom domains via Cloudflare API with automatic SSL certificates
- Cache: Cloudflare Cache API integration for high-performance caching
- Rate Limit (optional): Workers Rate Limiter bindings wired into AuthHero's
pre-login,brute-force, andpre-user-registrationscopes - Logs (optional): Two options for authentication logs:
- Analytics Engine: Low-latency writes with SQL querying (90-day retention). Also returns the log-derived
analyticsandstatsadapters. - R2 SQL: Long-term storage with unlimited retention
- Analytics Engine: Low-latency writes with SQL querying (90-day retention). Also returns the log-derived
- Edge Compatible: Works in Cloudflare Workers and standard Node.js environments
- Global Distribution: Leverage Cloudflare's global network
Installation
npm install @authhero/cloudflare-adapterQuick Start
Basic Setup
import createAdapters from "@authhero/cloudflare-adapter";
const adapters = createAdapters({
// Custom domains configuration (required)
zoneId: "your-cloudflare-zone-id",
authKey: "your-cloudflare-api-key",
authEmail: "your-cloudflare-email",
customDomainAdapter: yourDatabaseCustomDomainsAdapter,
// Cache configuration (optional)
cacheName: "default",
defaultTtlSeconds: 3600,
keyPrefix: "authhero:",
// Rate limit bindings (optional — see Rate Limit Adapter docs)
// Bindings are declared in wrangler.toml under [[unsafe.bindings]] with
// type = "ratelimit". Any scope you omit fails open.
rateLimitBindings: {
"pre-login": env.RATE_LIMIT_PRE_LOGIN,
"brute-force": env.RATE_LIMIT_BRUTE_FORCE,
"pre-user-registration": env.RATE_LIMIT_PRE_REGISTRATION,
},
// Analytics Engine logs configuration (optional, recommended for Workers)
analyticsEngineLogs: {
analyticsEngineBinding: env.AUTH_LOGS,
accountId: env.CLOUDFLARE_ACCOUNT_ID,
apiToken: env.ANALYTICS_ENGINE_API_TOKEN,
},
// OR R2 SQL logs configuration (optional, for long-term storage)
r2SqlLogs: {
authToken: process.env.R2_SQL_AUTH_TOKEN,
warehouseName: process.env.R2_WAREHOUSE_NAME,
namespace: "default",
tableName: "logs",
},
});
// Use the adapters. `analytics` and `stats` are only present when
// `analyticsEngineLogs` is configured — they are derived from the same events.
const { customDomains, cache, logs, analytics, stats } = adapters;Adapters
Custom Domains
Manage custom domains through the Cloudflare API with automatic SSL certificate provisioning and DNS configuration.
Key Features:
- Automatic SSL certificate management
- DNS configuration through Cloudflare API
- Enterprise and non-enterprise mode support
- Full CRUD operations for domains
Learn more about Custom Domains →
Cache
High-performance caching using Cloudflare's Cache API for fast data retrieval at the edge.
Key Features:
- Edge-based caching for minimal latency
- Configurable TTL per cache entry
- Key prefix support for namespacing
- Simple get/set/delete operations
Rate Limit (Optional)
Wire AuthHero's pre-login, brute-force, and pre-user-registration scopes to Cloudflare Workers Rate Limiter bindings. Pass rateLimitBindings to createAdapters and the returned object will include a rateLimit field that you merge into your data adapter. The pre-login scope is additionally gated by the tenant's attack_protection.suspicious_ip_throttling.enabled flag.
Key Features:
- Short-window abuse protection at the edge (10s or 60s windows)
- Per-scope binding so limits differ by abuse vector
- Fails open if a binding is misconfigured or absent
Learn more about the Rate Limit adapter →
Logs (Optional)
Choose between two logging solutions based on your needs:
Analytics Engine
Best for real-time analytics and recent logs with near-zero write latency.
Key Features:
- Fire-and-forget writes (~0ms latency)
- SQL-based querying
- 90-day retention (configurable)
- Free tier available
- Ideal for Workers
Learn more about Analytics Engine →
R2 SQL
Best for long-term storage and compliance requirements with unlimited retention.
Key Features:
- Unlimited retention period
- Apache Iceberg format
- Pipeline-based ingestion
- SQL querying via R2 SQL
- Pay-as-you-go pricing
Complete Integration Example
Here's a complete example integrating all adapters:
import createAdapters from "@authhero/cloudflare-adapter";
import { createKyselyAdapter } from "@authhero/kysely-adapter";
// Create database adapter for custom domains
const database = createKyselyAdapter(db);
// Create Cloudflare adapters
const cloudflareAdapters = createAdapters({
// Custom domains
zoneId: process.env.CLOUDFLARE_ZONE_ID!,
authKey: process.env.CLOUDFLARE_AUTH_KEY!,
authEmail: process.env.CLOUDFLARE_AUTH_EMAIL!,
customDomainAdapter: database.customDomains,
// Cache
cacheName: "authhero-cache",
defaultTtlSeconds: 3600,
keyPrefix: "authhero:",
// Logs (choose one: Analytics Engine or R2 SQL)
analyticsEngineLogs: {
analyticsEngineBinding: env.AUTH_LOGS,
accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
apiToken: process.env.ANALYTICS_ENGINE_API_TOKEN!,
},
});
// Use in your application
export const dataAdapters = {
...database,
cache: cloudflareAdapters.cache,
customDomains: cloudflareAdapters.customDomains,
// The three log-derived adapters must move together — see the note below
logs: cloudflareAdapters.logs,
analytics: cloudflareAdapters.analytics,
stats: cloudflareAdapters.stats,
rateLimit: cloudflareAdapters.rateLimit, // optional — undefined if no bindings configured
};Move logs, analytics and stats together
Configuring analyticsEngineLogs makes createAdapters return all three of logs, analytics and stats, because all three are derived from the same log events. If you forward only logs and leave analytics/stats on the SQL database adapter, /api/v2/stats/daily, /api/v2/stats/active-users and the analytics endpoints keep reading the SQL logs table — which no longer receives writes once logs go to Analytics Engine — and report zeros.
Environment Variables
# Custom Domains (required)
CLOUDFLARE_ZONE_ID=your_zone_id
CLOUDFLARE_AUTH_KEY=your_api_key
CLOUDFLARE_AUTH_EMAIL=your_email
CLOUDFLARE_ENTERPRISE=false
# Cache (optional)
CACHE_NAME=default
CACHE_DEFAULT_TTL=3600
CACHE_KEY_PREFIX=authhero:
# Analytics Engine Logs (optional)
CLOUDFLARE_ACCOUNT_ID=your_account_id
ANALYTICS_ENGINE_API_TOKEN=your_analytics_token
ANALYTICS_ENGINE_DATASET=authhero_logs
# R2 SQL Logs (optional, alternative to Analytics Engine)
PIPELINE_ENDPOINT=https://your-stream-id.ingest.cloudflare.com
R2_SQL_AUTH_TOKEN=your_r2_sql_token
R2_WAREHOUSE_NAME=your_warehouse_name
R2_SQL_NAMESPACE=default
R2_SQL_TABLE=logsTypeScript Support
The package includes full TypeScript definitions:
import type {
CloudflareConfig,
CloudflareAdapters,
R2SQLLogsAdapterConfig,
AnalyticsEngineLogsAdapterConfig,
AnalyticsEngineDataset,
} from "@authhero/cloudflare-adapter";Best Practices
1. Error Handling
Always implement proper error handling:
try {
await customDomains.create(tenantId, domainData);
} catch (error) {
// Log error for debugging
console.error("Failed to create domain:", error);
// Optionally log to your logs adapter
if (logs) {
await logs.create(tenantId, {
type: "api_error",
date: new Date().toISOString(),
ip: request.ip,
user_agent: request.headers["user-agent"],
isMobile: false,
description: `Failed to create domain: ${error.message}`,
});
}
throw error;
}2. Cache Strategy
Implement cache-aside pattern for optimal performance:
async function getDataWithCache<T>(
key: string,
fetcher: () => Promise<T>,
ttl: number = 300,
): Promise<T> {
// Try cache first
const cached = await cache.get<T>(key);
if (cached) return cached;
// Fetch from source
const data = await fetcher();
// Cache for next time
await cache.set(key, data, ttl);
return data;
}3. Logging Strategy
Choose the right logging solution for your use case:
- Analytics Engine: Real-time dashboards, recent activity monitoring
- R2 SQL: Compliance, audit trails, long-term analytics
- Both: Use passthrough adapter to write to both simultaneously