Quickback Docs

Auth

Better Auth on Cloudflare — authentication, sessions, organizations, and plugins for your Quickback API.

Quickback Stack uses Better Auth for authentication, running on Cloudflare Workers with D1 as the session store.

Overview

Better Auth provides:

  • Email/password authentication
  • Session management with cookies
  • Multi-tenant organizations with roles
  • Plugin ecosystem for passwordless auth, passkeys, and more

Configuration

Auth is configured in your quickback.config.ts:

import { defineAuth, defineConfig, defineRuntime, defineDatabase } from "@quickback/compiler";

export default defineConfig({
  name: "my-app",
  providers: {
    runtime: defineRuntime("cloudflare"),
    database: defineDatabase("cloudflare-d1"),
    auth: defineAuth("better-auth", {
      emailAndPassword: { enabled: true },
      plugins: ["emailOtp", "passkey", "magicLink"],
    }),
  },
});

Auth Base Path

All Better Auth routes are served under:

/auth/v1/*

Common endpoints:

  • POST /auth/v1/sign-in/email — Email/password sign in
  • POST /auth/v1/sign-up/email — Create account
  • GET /auth/v1/get-session — Get current session
  • POST /auth/v1/sign-out — Sign out

Organization Roles

RoleDescription
ownerFull access — can delete the organization and transfer ownership
adminFull access — can manage members and resources, cannot delete the organization
memberStandard access — read and limited write, cannot delete or manage members

These are Better Auth's built-in organization roles — no configuration needed. The creatorRole defaults to owner.

Tip: Account UI's role picker uses these exact three roles. Use ["owner", "admin", "member"] in your Access rules so generated projects plug into Better Auth and Account UI seamlessly.

Roles are used throughout the security layers — in Access rules and Firewall owner checks. Roles are an API gate: they do not reach SQL. Generated Row Level Security is a backstop that fences tenant scope, soft delete and lane admission only, and no generated policy body names a role. Better Auth's own API is the gate for its tables.

Next Steps

  • Plugins — Email OTP, passkeys, magic links, and more
  • Security — Cookies, rate limiting, cross-domain auth

Compiler-managed auth versions and upgrades

The compiler pins Better Auth, its schema CLI, and the generated companion packages to 1.7.3. Recompile and install the emitted dependencies together; remove older Better Auth overrides from build.dependencies. An incompatible override is rejected because it would run a different auth version from the one that generated your schema.

For an existing project, retain its quickback/drizzle migration history and use the updated CLI when recompiling. The CLI generates the upgrade locally; migration history stays on your machine. Apply the newly generated auth migration before starting the updated Worker. Existing migration SQL is preserved.

Upgrading from 1.6

The upgrade adds the current JWT/OAuth schema, including JWKS alg/crv and resource-bound grants. The account table is unchanged — accounts are still keyed by (providerId, accountId), so there is no identity backfill and no credential reset.

Existing OAuth grants without a recorded resource set are revoked during the upgrade; clients must authorize again. Client registrations and ordinary sessions are retained. The compiler maps its audience configuration into the 1.7 resources option and preserves the previous global resource allowlist for clients (enforcePerClientResources: false). Authorization-code and refresh grants still bind resources and reject widening to an unauthorized resource. Projects opting into per-client resource restrictions must link their clients to resources using the OAuth provider's resource administration APIs.

Projects compiled between 2026-09-05 and the 1.7.3 pin

Better Auth 1.7.0–1.7.2 briefly keyed accounts by an issuer column with a unique (issuer, accountId) index. 1.7.3 restored the 1.6 account schema, so only projects compiled during that window carry the extra column.

Recompiling emits one appended migration that drops the identity index and then the issuer column — index first, because SQLite refuses to drop an indexed column. It is generated from the schema diff, so a project that never had issuer gets nothing, and prior migration SQL is untouched. No backfill is needed in either direction: issuer was always derived from providerId, and 1.7.3 never reads it.

Apply it before the updated Worker serves traffic. A leftover NOT NULL issuer column is an outage on 1.7.3 — Better Auth validates its schema at init and refuses a required column it never writes.

Generated projects keep that validation on at runtime (advanced.database.validateSchema: !!env in src/lib/auth.ts). It is off in exactly one case: when the schema CLI imports auth.ts there are no bindings, so the adapter holds a stub with no tables to check and the validator would report every table as missing.

The bundled Account UI uses signIn.social and linkSocial for generic OAuth providers, including Cloudflare. Their callback is /auth/v1/oauth2/callback/cloudflare; update the registered redirect URI when upgrading an existing Cloudflare login integration.

On this page