import { tracing } from 'cloudflare:workers' import { createConfiguredEmailSender, isEmailDeliveryReady } from '@server/adapters/gateways/email/sender' import { createSecretCipher } from '@server/adapters/gateways/secrets' import { createDrizzleConfigzRepository, readBuiltInProviderSettings } from '@server/adapters/repos/configz' import { createConnectorRepository } from '@server/adapters/repos/connectors' import { type Auth, createAuth } from '@server/auth' import { createDeps } from '@server/composition' import { createDb } from '@server/db/client' import { type Env, type RuntimeConfig, validateEnv } from '@server/env' import { createApp, healthStatus } from '@server/http/app' import { readCorrelationId } from '@server/http/correlation' import { withRequestErrorBoundary } from '@server/http/request-error-boundary' import { withWorkerRequestMetrics } from '@server/http/worker-request-metrics' import { withinInitializationDeadline } from '@server/initialization-deadline' import { processAccountDeletionCleanup } from '@server/usecases/account-deletion' import { reconcileRealmrootResourceServer, synchronizeEnabledResourceScopeRegistries, } from '@server/usecases/authorization' import { defaultBuiltInProviders } from '@server/usecases/configz' import { loadAuthConnectorConfig } from '@server/usecases/connectors' import { publishWebhookEvent } from '@server/usecases/webhooks' import { managementBuiltInProviderSettingsSchema } from '@shared/api/management' const routers = new WeakMap>() let cachedAuth: Auth | null = null let cachedKey: string | null = null let cachedStaticKey: string | null = null let cachedValidatedAt = 0 let cachedDb: D1Database | null = null let cachedEmail: Env['EMAIL'] | null = null let cachedSecurityPolicy: RuntimeConfig['securityPolicy'] | null = null let cachedSecurityPolicyDb: D1Database | null = null let cachedSecurityPolicyAt = 0 let reconciledBaseURL: string | null = null const dynamicConfigRefreshIntervalMs = 5_000 const publicMetadataCacheControl = 'public, max-age=15, stale-while-revalidate=15, stale-if-error=86400' const cachedPublicMetadataPaths = new Set(['/.well-known/agent-configuration']) const emailVerificationPolicyPaths = new Set([ '/api/auth/sign-up/email', '/api/auth/sign-in/email', '/api/auth/sign-in/username', ]) export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { return withWorkerRequestMetrics(request, () => withRequestErrorBoundary(request, async () => { // Liveness probe answers from the process alone — before any D1 read — so it // reports the worker is up even when the database is unmigrated or down. const path = new URL(request.url).pathname if (path === '/api/health') return Response.json(healthStatus) const publicMetadataCache = request.method === 'GET' && cachedPublicMetadataPaths.has(path) ? await caches.open('realmroot-public-metadata') : null const cached = await publicMetadataCache?.match(request) if (cached) return cached return tracing.enterSpan('realmroot.request.prepare', async (span) => { span.setAttribute('url.path', path) const config = validateEnv(env, request.url) const correlationId = readCorrelationId(request.headers.get('x-correlation-id')) ?? undefined if (correlationId) span.setAttribute('realmroot.correlation_id', correlationId) const deps = createDeps(env, config, correlationId) const resourceTokenRequest = request.headers.get('authorization')?.startsWith('DPoP ') ?? false const [, securityPolicy] = await Promise.all([ reconcileResourceOnce(deps, config.baseURL), tracing.enterSpan('realmroot.security-policy.load', () => resourceTokenRequest ? Promise.resolve(cachedSecurityPolicy ?? config.securityPolicy) : loadSecurityPolicy(env, deps), ), ]) const auth = await tracing.enterSpan('realmroot.auth.prepare', () => getAuth( env, { ...config, securityPolicy }, deps, resourceTokenRequest, emailVerificationPolicyPaths.has(path), ), ) let app = routers.get(auth) if (!app) { app = createApp(auth, (c) => c.env.realmrootRequestDeps, { baseURL: config.baseURL, trustedOrigins: config.trustedOrigins, securityPolicy, realmrootResourceReconciled: true, }) routers.set(auth, app) } const response = await tracing.enterSpan('realmroot.router.dispatch', () => app.fetch(request, { ...env, realmrootRequestDeps: deps }, ctx), ) if (!publicMetadataCache || !response.ok) return response const headers = new Headers(response.headers) headers.set('Cache-Control', publicMetadataCacheControl) const cacheable = new Response(response.body, { status: response.status, statusText: response.statusText, headers, }) ctx.waitUntil(publicMetadataCache.put(request, cacheable.clone())) return cacheable }) }), ) }, async scheduled(_controller: ScheduledController, env: Env, ctx: ExecutionContext) { const config = validateEnv(env, env.BETTER_AUTH_URL ?? 'https://scheduled.realmroot.invalid') const deps = createDeps(env, config) ctx.waitUntil(processAccountDeletionCleanup(deps)) if (_controller.cron === '0 3 * * *') ctx.waitUntil(synchronizeEnabledResourceScopeRegistries(deps)) }, } async function getAuth( env: Env, config: RuntimeConfig, deps: ReturnType, resourceTokenRequest = false, forceDynamicRefresh = false, ): Promise { const staticKey = [ config.authSecret, config.baseURL, config.emailFrom ?? '', config.emailFromName ?? '', config.trustedOrigins.join(','), JSON.stringify(config.securityPolicy), ].join('\n') if ( cachedAuth && cachedStaticKey === staticKey && cachedDb === env.DB && cachedEmail === env.EMAIL && !forceDynamicRefresh && (resourceTokenRequest || Date.now() - cachedValidatedAt < dynamicConfigRefreshIntervalMs) ) { return cachedAuth } const db = createDb(env.DB) const configz = createDrizzleConfigzRepository(db) const [connectors, validAudiences, storedBuiltInProviders, emailSettings] = await withinInitializationDeadline( Promise.all([ loadAuthConnectorConfig(createConnectorRepository(db, createSecretCipher(config.credentialEncryptionKey))), loadValidAudiences(env.DB, config.baseURL), readBuiltInProviderSettings(db), configz.getEmailSettings(), ]), 'configuration', ) const builtInProviders = managementBuiltInProviderSettingsSchema.parse( mergeBuiltInProviders(defaultBuiltInProviders, storedBuiltInProviders), ) const cacheKey = [ config.authSecret, config.baseURL, config.emailFrom ?? '', config.emailFromName ?? '', config.trustedOrigins.join(','), JSON.stringify(config.securityPolicy), JSON.stringify(builtInProviders ?? {}), JSON.stringify(emailSettings), connectors.cacheKey, validAudiences.join(','), ].join('\n') if (!cachedAuth || cachedKey !== cacheKey || cachedDb !== env.DB || cachedEmail !== env.EMAIL) { const fallbackEmailSender = config.emailFrom ? { from: config.emailFrom, ...(config.emailFromName ? { fromName: config.emailFromName } : {}) } : undefined const emailSender = createConfiguredEmailSender(env.EMAIL, () => configz.getEmailSettings(), fallbackEmailSender) const auth = createAuth( db, deps.ids, config.authSecret, config.baseURL, config.trustedOrigins, emailSender, config.securityPolicy, connectors, { builtInProviders, emailDeliveryReady: isEmailDeliveryReady(env.EMAIL, emailSettings, fallbackEmailSender), twoFactorEmailOtpEnabled: config.securityPolicy.mfa.emailOtpEnabled, validAudiences, externalHttp: deps.externalHttp, publishWebhookEvent: async (event, data) => { await publishWebhookEvent(deps, event, data) }, }, ) // Better Auth now performs database I/O during initialization. Share only a // ready instance: a failed/cancelled request must not own later requests' I/O. await withinInitializationDeadline(auth.$context, 'provider') cachedAuth = auth cachedKey = cacheKey cachedStaticKey = staticKey cachedDb = env.DB cachedEmail = env.EMAIL } cachedValidatedAt = Date.now() return cachedAuth } async function loadSecurityPolicy(env: Env, deps: ReturnType) { if ( cachedSecurityPolicy && cachedSecurityPolicyDb === env.DB && Date.now() - cachedSecurityPolicyAt < dynamicConfigRefreshIntervalMs ) { return cachedSecurityPolicy } cachedSecurityPolicy = await withinInitializationDeadline(deps.security.getPolicy(), 'security policy') cachedSecurityPolicyDb = env.DB cachedSecurityPolicyAt = Date.now() return cachedSecurityPolicy } async function reconcileResourceOnce(deps: ReturnType, baseURL: string) { if (reconciledBaseURL === baseURL) return await withinInitializationDeadline( tracing.enterSpan('realmroot.resource.reconcile', () => reconcileRealmrootResourceServer(deps, baseURL)), 'resource reconciliation', ) reconciledBaseURL = baseURL } async function loadValidAudiences(db: D1Database, baseURL: string) { const result = await db .prepare('SELECT resource_url FROM api_resource WHERE enabled = 1 ORDER BY resource_url') .all<{ resource_url: string }>() return [`${baseURL}/api/auth/oauth2/userinfo`, baseURL, ...result.results.map((row) => row.resource_url)] } function mergeBuiltInProviders( defaults: typeof defaultBuiltInProviders, stored: unknown, ): typeof defaultBuiltInProviders { if (!stored || typeof stored !== 'object' || Array.isArray(stored)) return defaults const input = stored as Partial> return { email: mergeProvider(defaults.email, input.email), phone: mergeProvider(defaults.phone, input.phone), web3Wallet: mergeProvider(defaults.web3Wallet, input.web3Wallet), passkey: mergeProvider(defaults.passkey, input.passkey), oneTap: mergeProvider(defaults.oneTap, input.oneTap), } } function mergeProvider>(defaults: T, stored: unknown): T { if (!stored || typeof stored !== 'object' || Array.isArray(stored)) return defaults return { ...defaults, ...stored } }