Skip to content

medreseli/derbent

Repository files navigation

Derbent Auth Engine

Self-hosted authentication for Cloudflare Workers.

  • Cross-subdomain SSO
  • OAuth login (GitHub)
  • Session-based auth (no JWTs)
  • Session hijack protection
  • Built using D1 + KV
  • Audit logging
User
  │
  ▼
App Worker ── Service Binding ──► Derbent Auth Worker
  │                                │
  │                                ├── KV (sessions)
  │                                └── D1 (users + audit logs)
  ▼
Cloudflare Cache

Getting Started

You can deploy Derbent using the automated 1-click deploy button or manually via the CLI.

Option 1: 1-Click Deploy (Recommended)

Deploy to Cloudflare

The button above will automatically clone this repository to your GitHub account, provision your Cloudflare resources (KV, D1, Queues), run the required database migrations, and safely prompt you for the necessary environment variables.

Once deployed, clone your new repository locally and proceed to Step 3: Registering Your Apps below to configure your allowed applications.


Option 2: Manual Setup

1. Setup

git clone https://github.com/medreseli/derbent.git
cd derbent
npm install

2. Infrastructure Setup

You need to create your own Cloudflare resources for this instance:

  1. KV: npx wrangler kv namespace create KV
  2. D1: npx wrangler d1 create db-derbent
  3. Queue: npx wrangler queues create derbent-email-queue
  4. Paste the generated IDs into your wrangler.jsonc.

3. Registering Your Apps

Derbent uses a strict whitelist to determine which apps are allowed to authenticate. Open src/config/apps.ts and add your applications (e.g., geveze, namedar) to the ALLOWED_APPS array and REGISTERED_APPS object along with their production and development URLs.

4. Environment Configuration

Create a .dev.vars file for development. For production, use wrangler secret.

# APP_ENV: 'development' or 'production'
APP_ENV=development
LOG_LEVEL=debug
APP_NAME=Derbent Auth
COOKIE_DOMAIN=localhost
BASE_URL=http://localhost:8787
RESEND_API_KEY=re_your_api_key_here
RESEND_DOMAIN=your-verified-domain.com
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret

Note for GitHub Login: You should create 2 OAuth apps in GitHub. One for local testing and the other for production. The Authorization callback URL format is https://<your-domain>/auth/github/callback. When you deploy your app, do not forget to use the production OAuth app's client ID and secret.

5. Database

Prepare database:

npx wrangler d1 migrations apply db-derbent --local

6. Run

Run locally:

npm run dev

Integration for Consuming Apps

Derbent acts as a sidecar for your other services. Use Service Bindings to connect them without touching the public internet.

1. Wrangler Configuration

In your consuming app's wrangler.jsonc, add the service binding:

{
	"services": [
		{
			"binding": "DERBENT_SERVICE",
			"service": "derbent", // Name of the Derbent Worker
		},
	],
}

2. Verification Middleware

Every consuming app (e.g., geveze, namedar) should use the following pattern to verify users. Important: You must pass the end-user's IP and User-Agent using the Derbent-Client-* headers to maintain audit logging and session hijack protection.

export async function verifyWithDerbent(c: Context, appId: string) {
	const cache = caches.default;
	const cookie = c.req.header('Cookie') || '';

	// If the cookie is empty, we can skip the fetch entirely to save CPU
	if (!cookie) {
		return c.json({ error: 'Unauthorized' }, 401);
	}

	const clientIp = c.req.header('cf-connecting-ip') || '127.0.0.1';
	const clientUa = c.req.header('user-agent') || 'unknown';

	// SECURITY: Cloudflare Cache API ignores the 'Vary: Cookie' header.
	// To prevent cross-session leaking, we create a unique cache key by hashing the cookie.
	const encoder = new TextEncoder();
	const data = encoder.encode(cookie);
	const hashBuffer = await crypto.subtle.digest('SHA-256', data);
	const hashArray = Array.from(new Uint8Array(hashBuffer));
	const cookieHash = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');

	// PERFORMANCE: Use the consuming Worker's actual hostname to prevent DNS lookup penalties in the Cache API.
	const currentUrl = new URL(c.req.url);
	const cacheUrl = new URL(`${currentUrl.origin}/_internal_auth_cache`);
	cacheUrl.searchParams.set('app_id', appId);
	cacheUrl.searchParams.set('cookie_hash', cookieHash);
	const cacheKey = new Request(cacheUrl.toString());

	let response = await cache.match(cacheKey);

	if (!response) {
		// The actual request to Derbent via Service Binding.
		const fetchReq = new Request(`https://auth.internal/internal/verify?app_id=${appId}`, {
			headers: {
				Cookie: cookie,
				'Derbent-Client-IP': clientIp,
				'Derbent-Client-UA': clientUa,
			},
		});

		response = await c.env.DERBENT_SERVICE.fetch(fetchReq);

		if (response.ok) {
			// Cache the response against our unique cacheKey
			await cache.put(cacheKey, response.clone());
		}
	}

	return response;
}

Note: Derbent sends Vary: Cookie and Cache-Control: private, max-age=60 by default.


🧪 Testing

npm run test

Architecture & Decisions

  • No JWTs: Opaque tokens only.
  • Stateful: Session data stored in Cloudflare KV.
  • Root Domain Cookies: Scoped to .yourdomain.com for cross-subdomain SSO.
  • Isolation: Supports both Global sso accounts and app-specific accounts.

Security & Business Logic

  1. SSO Priority: Once an sso account exists for an email, app-specific accounts for that email cannot be created.
  2. Rate Limiting: Protects against brute force.
  3. Audit Logging: Every action is recorded in the D1 audit_logs table.
  4. Hijack Prevention: Sessions are bound to User-Agent and IP.

Use Cases

Derbent works well for:

• SaaS apps on Cloudflare Workers
• Multi-subdomain applications
• Edge-native APIs
• Self-hosted authentication systems
• Replacing Auth0 for Workers projects

About

Self-hosted authentication for Cloudflare Workers. Session-based auth with KV + D1, cross-subdomain SSO, GitHub OAuth, and audit logging — without JWT complexity.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors