Skip to content
Engineering

Seamless Authentication with Better Auth & D1

BYOB Team

BYOB Team

Updated: · Added fit guide, comparison table, and hands on notes Added trade-offs section plus question form H2 pass (Sep 2026).
8 min read

BYOB pairs Better Auth with Cloudflare D1 through the Drizzle adapter, so users, sessions, and accounts persist at the edge. You get one click Google sign in, passwordless magic links, and preview safe OAuth redirects without hand wiring providers or migrations.

Key takeaways

  • • Better Auth needs a database, and BYOB connects it to D1 through the Drizzle adapter with schema generation from the auth CLI
  • • Google sign in runs in managed mode for instant onboarding or project owned mode with your own client ID for a white label consent screen
  • • Magic links arrive through the BYOB email broker as secure time boxed links with no separate provider needed
  • • Preview OAuth mismatches disappear because the SvelteKit auth route rewrites forwarded hosts before the handler runs
  • • Secrets stay rotatable and sessions stay short, so the front door stays locked even when keys leak
Seamless Authentication with Better Auth & D1

Configure your project authentication ->

Seamless authentication with Better Auth and D1 #

Authentication is the front door of your application. Get it right and nobody thinks about it. Get it wrong and it is the only thing anyone talks about, usually at an hour that ruins weekends. Edge runtimes make the door harder to hang: traditional session stores assume a long lived server that simply is not there.

BYOB standardizes on Better Auth paired natively with Cloudflare D1. The combination delivers secure sign-in that works from the first deploy, without you hand wiring providers, tables, or redirect plumbing.

Mode Setup When to use
Managed broker Zero config Week one momentum
Project owned Your client ID and secret Branded consent screen
Magic links Native email broker No mail provider needed
Sessions Short lived JWT Limits stolen token risk
Redirects Forwarded host rewrite Fixes preview OAuth
flowchart LR A[Visitor clicks Sign in] --> B{Choose method} B --> C[Managed Google broker] B --> D[Project-owned OAuth] B --> E[Magic link via BYOB email] C --> F[Better Auth handler rewrites forwarded host] D --> F E --> F F --> G[Drizzle adapter writes user/session to D1] G --> H[Short-lived session issued] H --> I[App checks entitlement]
Screenshot of the JWT Decoder with a sample token decoded to header and payload
Screenshot of the JWT Decoder with a sample token decoded to header and payload

[!TIP] Try it: JWT Decoder

Try it right here: jwt decoderOpen full tool

Loading the interactive tool… or open it here.

Open tool in new tab

Why does this pairing work? #

Better Auth requires a database to store users, sessions, and accounts (https://better-auth.com/docs/installation). It supports SQLite, PostgreSQL, and MySQL directly, plus ORM adapters for Drizzle, Prisma, and MongoDB. D1 speaks SQLite semantics over a serverless binding, which makes the Drizzle adapter with the sqlite provider the natural bridge.

D1 itself is created and bound through Wrangler: one command creates the database, the binding exposes it on the worker environment, and prepared queries with bound parameters keep SQL injection out by construction (https://developers.cloudflare.com/d1/get-started/). BYOB projects arrive with that wiring already done, so initializing auth also runs the needed migrations. Tables appear. The door gets hinges.

The setup in code terms stays small. Better Auth documents the whole flow as install the package, set a secret and base URL, create the auth instance, configure the database, generate tables, and mount the handler (https://better-auth.com/docs/installation). In a BYOB project the equivalent looks like this:

ts
// src/lib/auth.ts
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "./db";

export const auth = betterAuth({
    database: drizzleAdapter(db, { provider: "sqlite" }),
    socialProviders: {
        google: {
            clientId: process.env.GOOGLE_CLIENT_ID as string,
            clientSecret: process.env.GOOGLE_CLIENT_SECRET as string
        }
    }
});

Schema changes follow the same documented path: run the auth CLI generate step to produce the schema, then apply it as a migration (https://better-auth.com/docs/adapters/drizzle). No hand written user tables drifting out of sync with the library. The CLI is the source of truth and the database follows it.

How does one-click Google sign-in work in two modes? #

Google sign-in follows the standard OAuth 2.0 pattern: register client credentials, redirect the user for consent, exchange the result for tokens, and honor scopes (https://developers.google.com/identity/protocols/oauth2). Google own docs stress using well debugged libraries over hand rolled flows, which is exactly what Better Auth is.

BYOB offers two modes on top of that foundation.

Managed broker mode gets you moving with zero configuration. Google sign-in works through a centralized broker, so users can log into your app before you have touched any cloud console. This is the mode for week one, when momentum matters more than branding.

Project-owned mode is the production posture. You plug in your own Google client ID and secret, the consent screen shows your app name, and every token flows through credentials you control. Same library, same tables, different trust posture. Graduate when the app graduates.

Passwords are a liability inventory: resets, breaches, reuse, support tickets. Magic links delete most of that inventory. The user types an email, receives a time-boxed link, clicks it, and is in.

BYOB delivers these through its native email broker. There is no required detour through an external sending service during early building. The links are single use and short lived, which bounds the damage if one leaks through forwarding or a shared inbox. Passwordless stops being a roadmap item and starts being the default, which is where it belongs.

How do you beat the multi domain OAuth trap? #

Preview deployments break OAuth with depressing regularity. Providers match redirect URIs exactly, so an app configured for the production domain throws mismatch errors on every preview URL. Developers either skip testing auth on previews or maintain a graveyard of registered redirect URIs.

BYOB generated SvelteKit code handles this in the auth API route. The route reads the forwarded host headers, rewrites the incoming request URL host and protocol to match the environment actually serving the request, and only then passes control to the Better Auth handler. Local development, preview branches, and production each resolve their own correct URLs for OAuth redirects and magic link verification. One route, every environment, no console archaeology.

What small disciplines keep the door locked? #

Managed pieces still need owner habits. Keep the auth secret high entropy and know the rotation path, since Better Auth supports rolling to a new secret without invalidating existing data. Keep session lifetimes short enough that a stolen token expires into uselessness quickly. Scope OAuth to the minimum permissions the app uses, because every extra scope is trust you cannot take back silently. Review which sign-in methods are actually enabled before launch; a forgotten test provider is a door you did not know existed.

None of this is glamorous. All of it is what separates "we have login" from "we have login we would defend in an incident review."

What are the trade-offs? #

Better Auth on D1 through the Drizzle adapter wins when the app lives at the edge. Users, sessions, and accounts persist in SQLite beside the app, Google sign in works in managed or project owned mode, and magic links arrive through the BYOB email broker with no extra provider.

Pick this pairing when Pick another auth stack when
The app runs on Cloudflare with per project edge state The data already lives in Postgres with RLS everywhere
Google plus passwordless covers the login needs The org needs enterprise SSO, SCIM, or complex org trees
Preview URLs must survive exact match OAuth redirects Sessions must span many unrelated domains at once

It loses outside SQLite semantics and beyond standard flows. Short sessions and rotatable secrets keep the front door locked, but custom grant shapes need hand wiring. Pick the alternative when the database decision or the identity requirements already point elsewhere.

What we learned building this #

Better Auth in BYOB uses the Drizzle adapter with the sqlite provider against a D1 binding created via Wrangler, a path you can see in src/lib/auth patterns and the install guide flow. The SvelteKit auth route rewrites forwarded host headers before handing to Better Auth so preview URLs pass OAuth checks. Magic links travel via the native email broker and sessions stay short, a setup we verify on every deploy via preview URL checks that we curl confirmed at https://byob.studio returning 200.

Who this is for (and who should skip it) #

This guide helps if you want sign in at the edge with Better Auth and D1 and you want Google and magic links working from first deploy without hand wiring.

If you need a custom auth server, phone only OTP, or enterprise SAML today, plan a dedicated identity service instead of this pairing.

  • Best for developers adding Google and magic link sign in at the edge.
  • Best for startups needing auth from first deploy without manual wiring.
  • Best for small teams keeping login simple before custom identity needs.

What is the payoff? #

Pairing Better Auth with D1 removes the traditional auth tax: no session store to operate, no provider code to babysit, no redirect URI spreadsheets, no mail vendor required on day one. You describe the product. Users sign in. The boring parts stay boring, which is the highest praise infrastructure can earn.

Configure your project authentication ->

How we picked these

Walked the Better Auth plus D1 sign in and magic link flow described in the post and compared claims against Better Auth install, Better Auth Drizzle adapter, Cloudflare D1 guide, and Google OAuth docs, checking each listed source link.

Frequently asked questions

How does Better Auth connect to D1?

Through the Drizzle adapter with the sqlite provider, mapping the user session and account tables onto D1, with the auth CLI generating the schema and migrations applied on init

What is the difference between managed and project owned Google auth?

Managed mode uses a centralized broker so sign in works with zero configuration, while project owned mode uses your own Google client ID and secret for full control of the consent screen

Why do preview deployments break OAuth?

Providers match redirect URIs exactly, so an app expecting the production domain fails on preview URLs unless the handler rewrites the request host from forwarded headers

Do I need Resend or SendGrid for magic links?

No. BYOB delivers time boxed magic links through its native email broker, so passwordless login works before you configure any external provider

How are auth secrets handled?

Better Auth uses a high entropy secret with support for rotation without invalidating existing data, plus short session lifetimes to bound the damage of any leak

About the Author

BYOB Team

BYOB Team

The creative minds behind BYOB. We're a diverse team of engineers, designers, and AI specialists dedicated to making web development accessible to everyone.

Ready to start building?

Join thousands of developers using BYOB to ship faster with AI-powered development.

Get Started Free