TL;DR
Better Auth is the all-in-one default for 2026: framework-agnostic, plugin-rich (passkeys, MFA, multi-tenant orgs, magic links), with the smoothest TypeScript developer experience. OpenAuth (from the SST team) is the right call when you want a standalone OAuth/OIDC server you can deploy as its own service. Auth.js v6 (the rebrand of NextAuth) remains the path of least resistance specifically inside Next.js, especially when your auth needs are mostly social login + sessions. All three avoid the per-MAU pricing of hosted players. Choose based on whether you want auth as an embedded library (Better Auth, Auth.js) or as a separate service (OpenAuth).
Quick Verdict
| OpenAuth | Better Auth | Auth.js v6 | |
|---|---|---|---|
| Architecture | Standalone OAuth/OIDC server | Embedded library | Embedded library |
| Framework coupling | None | Framework-agnostic | Next-first, others supported |
| Built-in MFA / passkeys | Limited | Yes | Plugin-based |
| Multi-tenant orgs | DIY | Built-in | DIY |
| OAuth as identity provider | Yes (you ARE the IdP) | No | No |
| Session storage | DIY | Pluggable (DB, KV, JWT) | Pluggable (DB, JWT) |
| Database | DIY | Drizzle, Prisma, Mongo, Kysely | Drizzle, Prisma, Mongo |
| Best for | "I want my own IdP" | Most TypeScript apps | Existing Next.js apps |
Key Takeaways
- The split is "service vs library". OpenAuth runs as its own deploy. Better Auth and Auth.js are libraries that live inside your app process.
- Better Auth has won the "best DX" slot for embedded auth. Plugin breadth, type safety, and adapter coverage have outpaced Auth.js v6 for non-Next stacks.
- Auth.js v6 is the right keep-don't-rewrite call. If you're already on NextAuth and your needs are stable, the v6 upgrade gives you the cleanup without a migration.
- None of these solve enterprise SSO out of the box. SAML / SCIM and the enterprise admin surface still push teams toward WorkOS, Stytch, or Clerk at the upper end of the market.
What Each Library Actually Is
Better Auth
A framework-agnostic auth library written in TypeScript with a plugin architecture. The core surface (createAuth({...})) handles email/password, sessions, and OAuth. Plugins layer on passkeys, magic links, organizations + invitations, MFA (TOTP), API keys, rate limiting, and more.
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { passkey, organization, twoFactor } from "better-auth/plugins";
export const auth = betterAuth({
database: drizzleAdapter(db, { provider: "pg" }),
emailAndPassword: { enabled: true },
socialProviders: {
github: { clientId: process.env.GITHUB_ID!, clientSecret: process.env.GITHUB_SECRET! },
},
plugins: [passkey(), organization(), twoFactor()],
});
Adapter coverage: Next.js, Hono, Elysia, SvelteKit, Astro, TanStack Start. Type-safety is the standout — the client SDK is fully typed against the server config.
OpenAuth
OpenAuth is not an embedded auth library. It's a standalone OAuth 2.1 / OIDC server you run yourself (as a Worker, on AWS, or wherever). Your apps then act as OAuth clients.
import { issuer } from "@openauthjs/openauth";
import { GithubAdapter } from "@openauthjs/openauth/adapter/github";
import { handle } from "hono/aws-lambda";
const app = issuer({
providers: { github: GithubAdapter({ clientID, clientSecret, scopes: ["user:email"] }) },
storage: MemoryStorage(), // or DynamoDB, KV, etc.
subjects: { user: { type: "object", properties: { id: { type: "string" } } } },
});
export const handler = handle(app);
The model is "you become your own identity provider". This is right when:
- You have multiple frontends (web, mobile, CLI) that all need to share auth.
- You want full control over the OAuth flows.
- You're comfortable running auth as a separate deployment.
It's wrong when you want auth to feel like a library you import and forget about.
Auth.js v6
The rename of NextAuth into a more framework-neutral package, with v6 as the cleanup release. Adapter and provider catalogs are extensive; the developer experience inside Next.js is still where Auth.js shines hardest.
// auth.ts
import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
export const { auth, handlers, signIn, signOut } = NextAuth({
adapter: DrizzleAdapter(db),
providers: [GitHub],
});
v6 cleaned up the API surface (no more getServerSession boilerplate in Next.js App Router) and improved type inference. For non-Next adapters (SvelteKit, Express, etc.), it works but is less idiomatic than Better Auth in those stacks.
Decision Map
| If you... | Pick |
|---|---|
| Are building a single TypeScript app and want batteries-included auth | Better Auth |
| Need passkeys, MFA, and multi-tenant orgs without writing them | Better Auth |
| Run multiple apps (web + mobile + CLI) that share identity | OpenAuth |
| Are deeply invested in Next.js and want minimal disruption | Auth.js v6 |
| Have an existing NextAuth integration | Auth.js v6 (upgrade, don't migrate) |
| Want to truly be your own OAuth IdP | OpenAuth |
| Need enterprise SSO (SAML, SCIM) | None — see WorkOS / Stytch |
Plugin / Feature Comparison
| Feature | OpenAuth | Better Auth | Auth.js v6 |
|---|---|---|---|
| Email + password | DIY | Built-in | Plugin |
| Magic links | DIY | Built-in | Built-in |
| Passkeys (WebAuthn) | DIY | Built-in | Plugin |
| TOTP / MFA | DIY | Built-in | Plugin |
| Organizations / teams | DIY | Built-in | DIY |
| Rate limiting | DIY | Built-in | DIY |
| API keys | DIY | Built-in | DIY |
| OAuth client flows | Built-in (you ARE the server) | Built-in | Built-in |
The "DIY" rows in the OpenAuth column reflect its design — OpenAuth is primarily an OAuth/OIDC server, and feature richness lives at your application layer (which then talks to OpenAuth). Better Auth has the most batteries included.
Database & Adapter Reality
All three support pluggable storage; the practical question is which adapter is best-tested for your DB:
- Postgres + Drizzle: Better Auth (excellent), Auth.js v6 (good), OpenAuth (DIY).
- Postgres + Prisma: Better Auth, Auth.js v6 — both good.
- MongoDB: All three, with varying maturity.
- Cloudflare D1 / KV: Better Auth has dedicated adapters; OpenAuth is well-suited (since OpenAuth often deploys to Workers).
For broader auth-in-React context, see How to add authentication to a React app.
Migration Notes
- NextAuth v4 → Auth.js v6: mostly mechanical. Worth doing.
- NextAuth → Better Auth: requires schema migration and a rewrite of provider config. Worth doing for the plugin features (passkeys, orgs) if you need them.
- Roll-your-own → Better Auth: usually worth it. Roll-your-own auth in 2026 is hard to justify outside very narrow contexts.
- Hosted auth → Better Auth: viable if you're escaping per-MAU pricing. Pay attention to the SSO / SCIM gap if your enterprise customers needed it.
Who Should Pick What
- Solo dev / small team starting fresh: Better Auth. Lowest "I have to make 20 decisions before I have signup working" tax.
- Existing Next.js app on NextAuth v4: upgrade to Auth.js v6. Don't migrate to Better Auth unless you need plugin features.
- Org running multiple frontends sharing identity: OpenAuth as the central IdP, Better Auth (or any OAuth client library) inside each app.
- Enterprise B2B with SAML/SCIM requirements: skip self-hosted; pay for WorkOS or Stytch.
- Extreme "I want full control" team: roll your own with Lucia v3 or Better Auth and primitives like Oslo or Jose.
Verdict
In 2026, Better Auth is the default-pick for new TypeScript projects that want self-hosted auth — it has caught up with and in many areas surpassed Auth.js v6 for non-Next-specific stacks, and its plugin model handles the rest. Auth.js v6 remains the right answer when you're already on NextAuth and the upgrade path is what you need. OpenAuth is the specialized choice when "auth as its own service" is the architectural decision you've made — usually multi-app organizations that want a real IdP. None of these replace WorkOS-class enterprise auth for SAML/SCIM-driven sales motions.