Summary
This ADR proposes Better Auth as the authentication layer for the recipe site.
This is a decision about authentication, not the full backend or authorization model. It covers how users sign in and how account identity is represented. It does not decide:
- the eventual backend runtime or database
- the household / friend-sharing permission model
- whether any hosted auth operations platform is adopted later
Context
The recipe site is moving from a shared public collection toward user-specific features: favorites, cooking history, household sharing, and eventually friend sharing. That requires a real notion of user identity.
The auth choice needs to satisfy a small number of constraints:
- Config should live in code. This project leans heavily on agent-assisted workflows. Agents can read and edit TypeScript or Terraform; they cannot reliably own a SaaS dashboard.
- The choice should stay portable. Auth should not pre-decide the future backend ADR.
- Costs should stay predictable. A personal project does not need per-MAU pricing pressure.
- Social OAuth is the primary sign-in model. Google is the default path; GitHub is a useful secondary provider. Email/password is out of scope.
- MVP can tolerate some operational ownership. At inner-circle scale, owning the runtime is acceptable if it keeps the system simpler and more portable.
The market context matters mainly because it reduces decision risk. In 2025, the OSS TypeScript auth space consolidated around Better Auth: Lucia was sunset in March 2025, and Auth.js's official migration path now points to Better Auth after the September 2025 merger. In 2026, Better Auth is not an experimental contrarian pick; it is the main code-first OSS option in this category.
Decision
Use Better Auth as the recipe site's authentication library.
Auth configuration should live in repository code. Social OAuth credentials should live in the project's secrets layer. The exact runtime, database, and deployment target remain deferred to a future backend ADR.
At MVP, the intended scope is:
| Method | MVP? | Notes |
|---|---|---|
| Google OAuth | Yes | Expected primary sign-in path. |
| GitHub OAuth | Yes | Useful secondary path for technical friends. |
| Apple OAuth | Deferred | Add only if a real user need appears. |
| Email + password | No | Not worth the support and security overhead. |
| Magic links | Deferred | Adds email-delivery and link-consumption edge cases. |
| Passkeys | Deferred | Worth revisiting later, not necessary for MVP. |
| First-party MFA | Deferred | Social providers handle MFA at the identity-provider layer. |
The initial shape is intentionally small:
export const auth = betterAuth({
database: adapter(db),
socialProviders: {
google: {
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
},
github: {
clientId: env.GITHUB_CLIENT_ID,
clientSecret: env.GITHUB_CLIENT_SECRET,
},
},
plugins: [organization()],
});Better Auth's hosted Infrastructure product is explicitly out of scope for MVP. This ADR chooses the library, not a hosted control plane. Hosted dashboards, audit logs, or extra abuse protection can be a later operational decision if usage justifies them.
The authorization model remains a separate future ADR. Better Auth gives us stable identity and a household membership primitive; it does not settle how recipe visibility and sharing rules should work.
Why Better Auth
| Need | Better Auth fit |
|---|---|
| Code-first configuration | Auth is configured in TypeScript in the repo, which fits the project's agent-assisted workflow. |
| Portability | Built around modern web runtime primitives rather than a single framework or hosted platform. |
| Predictable cost | Self-hosted library, no per-MAU pricing curve. |
| Household sharing foundation | The organization plugin provides a ready-made membership primitive. |
| Future service / agent auth | Optional oauth-provider and api-key plugins exist if the project later exposes APIs or agent-facing tooling. |
| Maturity | The broader OSS TypeScript auth market has consolidated around it rather than away from it. |
Two points matter most:
- The source of truth is code. That makes the auth layer reviewable, diffable, and compatible with how the rest of the project is built.
- The operational shape matches the project. At MVP scale, a small self-hosted library is a better fit than paying for a large hosted auth platform before the problem actually demands one.
Cost Model
As of May 27, 2026, the cost picture is straightforward:
| Component | Cost |
|---|---|
| Better Auth library | $0. The framework itself is free and open source. |
| Better Auth Infrastructure | Out of scope for MVP. If adopted later: Starter is $0/month; Pro is $20/month; Enterprise is custom-priced. |
| Better Auth Infrastructure usage | Starter includes 1 dashboard seat, 10,000 audit logs/month, and 1,000 security detections/month. Pro includes unlimited seats, 20,000 audit logs/month then $0.0001/event, and 10,000 security detections/month then $0.001/event. |
| Google / GitHub OAuth | $0 at the auth-provider layer for normal use. |
| Email / SMS | $0 at MVP because email/password, magic links, and first-party MFA are deferred. If Better Auth Infrastructure Pro is adopted later, its pricing page lists $0.001/email and $0.09/SMS. |
| Database / session storage | Deferred to the backend ADR. This ADR does not pre-decide those costs. |
The practical implication is that the MVP auth decision does not add a mandatory monthly SaaS line item. The first real paid cost only appears if the project later chooses hosted operational features rather than staying with the library alone.
Cost Shape Of SaaS Alternatives
The reason pricing still weighs against the hosted options is not just "they cost money." It is that several of them introduce usage dimensions that map directly to the roadmap: monthly active users, machine-to-machine traffic, abuse checks, or paid operational add-ons.
As of May 27, 2026, the most decision-relevant cost signals are:
| Provider | Public pricing signal | Why it matters here |
|---|---|---|
| Clerk | Free up to 50,000 MRU. Pro starts at $20/month billed annually. M2M tokens are priced by usage after free monthly allowances: $0.001 per token creation after 2,500/month and $0.00001 per verification after 100,000/month. | Human-user auth is inexpensive early, but machine traffic introduces a separate metered cost axis. |
| Auth0 | Free up to 25,000 external active users. Pricing page shows 1,000 M2M authentications on Free and Essentials, 5,000 on Professional and Enterprise, with M2M add-ons on paid tiers. Essentials starts at $35/month. | The cost shape is more sensitive to API and agent usage because M2M is explicitly quota-based. |
| Kinde | Free includes 10,500 MAU. Pro starts at $25/month. Extra MAU are billed above the included tier. Free includes M2M applications; Pro advertises uncapped M2M tokens. | More favorable than Clerk/Auth0 on M2M cost, but still introduces a MAU-based pricing curve once usage grows past the free tier. |
| WorkOS | AuthKit is free up to 1 million monthly active users. Separate add-ons are priced independently, for example Radar starts free for 1,000 checks then $100 per 50,000 checks, and custom domains are $99/month. WorkOS Connect supports M2M, but the public pricing page does not expose token-based M2M pricing as clearly as Clerk or Auth0. | End-user auth cost is generous, but the overall price surface becomes more modular as more platform capabilities are turned on. |
| Supabase Auth | Free includes 50,000 MAU. Pro starts at $25/month with 100,000 MAU included, then $0.00325 per MAU. | Straightforward MAU pricing with predictable early cost, but still a per-user growth curve rather than infrastructure-shaped cost. |
The comparison is less about who has the lowest sticker price and more about which pricing axis becomes load-bearing if the roadmap succeeds. Better Auth's library avoids both a per-MAU curve and a separate M2M token meter. That matters because the planned future shape includes not just human users, but potentially agents, MCP tooling, background services, and third-party API access.
Alternatives Considered
Dashboard-First SaaS: Clerk, Kinde, WorkOS, Supabase Auth
These products offer polished managed auth, prebuilt UI, and vendor-owned operations. Those are real advantages.
They are rejected because their configuration surface mostly lives in vendor dashboards. That is the wrong fit for a project that wants auth to behave like the rest of the codebase. Supabase Auth is an even worse fit because it effectively pre-decides the wider backend platform.
Auth0
Auth0 is the strongest SaaS alternative and the clearest escape hatch if the self-hosted path stops being worth it.
Its first-party Terraform provider gives it the only serious code-first configuration story in the SaaS auth category. If a hosted platform becomes necessary, Auth0 is the most credible option because configuration can still live in code.
It is rejected for now because:
- the product is heavier than this project needs
- pricing at growth is less predictable; as of May 27, 2026 the pricing page shows a free tier up to 25,000 external active users, plus a separate M2M quota of 1,000 on Free/Essentials and 5,000 on Professional/Enterprise
- it adds a vendor relationship and platform surface area we do not currently need
Other OSS Libraries
- Auth.js: rejected because its own maintainers now point greenfield users toward Better Auth.
- Lucia: rejected because it was sunset in March 2025.
- Stack Auth: credible, but weaker on adoption, runtime story, and overall fit than Better Auth.
Roll Your Own
Rejected. Re-implementing OAuth, session management, CSRF protection, cookie handling, and account linking would be a large amount of undifferentiated work.
Consequences
Positive
- Auth config stays in the repo. Changes are reviewed in PRs instead of hidden in a dashboard.
- The backend ADR stays open. Auth does not force a database or hosting choice up front.
- Costs stay simple. There is no per-user SaaS pricing curve.
- Household sharing gets a head start. The organization primitive covers membership without forcing us to invent it from scratch.
- The support surface stays small. Social OAuth-first avoids password resets, password storage, and most first-party auth UX complexity.
Negative
- We own the runtime. Rate limiting, abuse mitigation, admin tooling, and data-deletion flows remain our responsibility unless we later adopt hosted operational tooling.
- Apple Sign In would add maintenance if adopted later. In particular, the Apple client secret rotation burden would sit with us rather than a SaaS provider.
- Dependency review matters. Better Auth moves quickly, so upgrade hygiene is part of the cost.
- There is some business-risk overhang. Better Auth is VC-backed; if the OSS path degrades, we may need to revisit sooner than planned.
When To Revisit
Revisit this ADR if any of the following become true:
- public signup materially increases abuse or support burden
- hosted audit logs, dashboards, or stronger managed security controls become necessary
- enterprise SSO / compliance requirements appear
- Better Auth's runtime support or maintenance quality drops
- a future backend ADR surfaces a hard incompatibility with this choice
If the self-hosted library path stops paying for itself, Auth0 is the documented fallback, because it preserves the same code-first operating model better than any other hosted option.