Skip to content

Better Auth: Should Authentication Live Inside Your TypeScript App?

Aug 22, 2026 1 min
TL;DR Better Auth trades a managed identity platform for an in-app library and your own database; that gives you control, but migrations, security updates, and incident response become your responsibility.
Table of Contents
  1. The minimum path: library, database, route
  2. The database is the identity system's skeleton
  3. Sessions: a cache is not revocation
  4. A plugin is a capability—and another attack surface
  5. Authentication ends where product authorization begins
  6. A library does not remove the security obligation
  7. Better Auth versus Clerk, WorkOS, and Stytch
  8. When it fits—and when it does not
  9. References

🌏 中文版

Better Auth is not another login API that moves your users into a vendor dashboard. It is an authentication and authorization framework embedded in a TypeScript application: routes execute in your service, core identity tables live in your database, and plugins add advanced capabilities. Its documentation describes it as a framework-agnostic TypeScript framework, not a hosted identity service (Better Auth Introduction).

That distinction drives the purchasing decision. Better Auth is not “free Clerk.” It is a structured foundation for operating password, OAuth, session, organization, and permission flows yourself. You gain control of data and behavior, while taking back schema migrations, email delivery, secrets, upgrades, and incident response.

The minimum path: library, database, route

For a Next.js application with PostgreSQL, a minimal server configuration looks like this:

// lib/auth.ts
import { betterAuth } from "better-auth";
import { Pool } from "pg";

export const auth = betterAuth({
  database: new Pool({ connectionString: process.env.DATABASE_URL }),
  emailAndPassword: { enabled: true },
  trustedOrigins: ["https://app.example.com"],
});

Mount its handler at /api/auth/[...all]:

// app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";

export const { GET, POST } = toNextJsHandler(auth);
// lib/auth-client.ts
import { createAuthClient } from "better-auth/react";

export const authClient = createAuthClient();

This follows the official integration: the same auth instance becomes the GET and POST handler, while server code can call auth.api.getSession({ headers }) (Next.js integration). Other frameworks use different adapters, but the lifecycle remains yours: your process receives the request, Better Auth verifies it, and your storage layer keeps the state.

The database is the identity system's skeleton

With a database configured, the core schema includes user, session, account, and verification; plugins may add tables or columns. The CLI commands have an important boundary: npx auth@latest migrate directly handles only the built-in Kysely adapter. With Prisma or Drizzle, generate produces a schema or SQL migration that should go through the ORM's normal review and deployment process (Database, CLI).

Adding an authentication feature can therefore be a production schema change. Pin versions, inspect generated diffs in CI, back up data, migrate staging first, and deploy compatible code. Blindly migrating on application startup is risky, and removing a plugin does not automatically make its tables safe to drop.

Database-free stateless operation exists, but most plugins require persistence. If the product already uses a relational database, keeping the user key, transaction relationships, and tenant foreign keys in one data model is one of Better Auth's strongest advantages.

Sessions: a cache is not revocation

The default is a traditional cookie session: the cookie carries a token and the server reads a session row. Sessions expire after seven days by default and slide after updateAge. A short-lived cookieCache can reduce reads, and secondary storage can hold sessions. Fully stateless validation avoids database reads, but immediate invalidation is harder; one documented way to invalidate all cookie-cached sessions is changing the cache version (Session Management).

The choice is not only about latency. Admin suspension, leaked passwords, departing employees, and privileged actions all need explicit revocation and freshness policies. Sensitive server operations should load and validate the session, require recent authentication when appropriate, and never trust only a client hook or a long-lived cookie.

A plugin is a capability—and another attack surface

Better Auth says its catalog contains more than 50 plugins across 2FA, passkeys, organizations, SSO, SCIM, API keys, and other areas (Plugins). The count is not an adoption argument. The important inventory is the data, endpoints, and operational obligations each plugin introduces.

  • Passkeys: install the separate @better-auth/passkey package, configure both server and client plugins, and run a migration. It uses SimpleWebAuthn; devices retain private keys while the server stores public-key credentials (Passkey plugin). The product still needs lost-device handling, credential management, and account recovery.
  • Organizations: provides organizations, members, invitations, roles, permissions, and optional teams. It adds organization/member/invitation tables and active organization/team fields to sessions. Your application still supplies the invitation-email function (Organization plugin).
  • SSO: the separate @better-auth/sso package supports SAML and OIDC, adds an ssoProvider schema, and offers domain verification. When combined with organizations, provider registration and automatic account linking have additional authorization and verified-domain constraints (SSO plugin). This is enterprise identity integration, not a boolean that completes the operational work.

Plugins accelerate composition, but each addition deserves a threat model, migration review, endpoint inventory, and upgrade test.

Authentication ends where product authorization begins

“Signed in” does not imply “may read this invoice.” The Organization plugin can define roles and resource/action permissions and exposes server APIs for invitations, members, and teams. Order ownership, row-level tenant isolation, and approval thresholds still belong to the application.

const session = await auth.api.getSession({ headers: request.headers });
if (!session) throw new Error("unauthenticated");

// Authorize using server-loaded membership and resource ownership.
// Never accept a client-supplied role or activeOrganizationId as truth.

In multi-tenant systems, an “active organization” should not be the only boundary. Every data query should carry a tenant predicate, and high-risk actions should re-check membership and permission. Hiding a button in the UI is user experience, not authorization.

A library does not remove the security obligation

Better Auth includes origin validation, SameSite=Lax and httpOnly cookies, OAuth state/PKCE, and rate limiting. Its security reference explicitly warns that disabling CSRF or origin checks exposes CSRF or open-redirect risk. Behind reverse proxies, trusted IP headers and proxy chains must also be configured correctly or attackers may spoof their source and evade controls (Security).

The library cannot own the entire system for you. The team still manages BETTER_AUTH_SECRET and OAuth secrets, TLS, backups, email deliverability, recovery, audit logs, dependency alerts, upgrades, and security reports. Owning the database also means owning deletion, export, retention, and access control. Without a named auth owner, control can quickly become unowned risk.

Better Auth versus Clerk, WorkOS, and Stytch

OptionPrimary tradeBest-aligned situation
Better AuthIn-app TypeScript library and your database; highly adaptable, self-operatedTypeScript backend, strong data-model requirements, and a team able to own security and upgrades
ClerkHosted users/sessions plus prebuilt UI and Elements; faster login UXFrontend-heavy teams avoiding auth edge cases; Clerk notes that custom flows require handling more logic and error states (How Clerk works)
WorkOSHosted AuthKit with enterprise SSO, Directory Sync, organizations, and provisioningB2B products facing many enterprise IdPs and IT onboarding workflows (Directory Provisioning)
StytchAPI-first hosted consumer/B2B authentication and sessionsTeams wanting custom API-driven flows without storing credentials and sessions themselves; its B2B API returns and extends Stytch sessions (Stytch B2B Authenticate)

This is not a feature-checklist contest. If every enterprise customer brings a distinct IdP, group mapping, and deprovisioning workflow, buying the operational layer from a vendor such as WorkOS can be cheaper than implementing the protocols. For product organizations and a limited number of SSO connections, Better Auth's plugins may be more direct. Clerk and Stytch shift substantial operational burden to a vendor, in exchange for less control over data models, pricing, and platform dependency.

When it fits—and when it does not

Better Auth fits teams that already run a TypeScript server, understand database migrations, need an owned user/tenant schema, and will assign someone to maintain authentication. It also fits products that cannot place core identity data in an external identity SaaS but do not want to reinvent password hashing, OAuth callbacks, and session endpoints.

If the goal is a polished sign-in this week, the team lacks security and on-call capacity, or the near-term enterprise roadmap requires extensive IdP onboarding, Directory Sync, compliance evidence, and support SLAs, a managed service is usually more pragmatic. Better Auth reduces the cost of reinventing an auth framework. It does not make an identity system maintenance-free.

References


中文版:Better Auth:把 TypeScript 認證放回應用程式裡,值得嗎?