Better Auth 深度拆解:当开源认证决定「不再做 NextAuth 的替代品」——从 Lucia 停更到 Vercel 收购,一个 TypeScript 全栈认证框架如何用「插件化架构 + 无供应商锁定」重新定义 Web 认证的终极形态
引言:认证领域的权力真空
2025 年 3 月,JavaScript 认证生态发生了一场地震。
Lucia——这个曾经被无数 Next.js 教程奉为圭臬的认证库——突然宣布停更。创始人 Pilcrow 在公告中写道:「我不再认为维护一个认证库是我应该花时间的地方。」这不是一次普通的弃坑,而是一个信号:JavaScript 生态的认证基础设施,正在经历一次范式级的重构。
在 Lucia 停更后的 18 个月里,认证领域涌现了大量替代方案。NextAuth.js(现改名 Auth.js)继续迭代,Clerk 和 Supabase Auth 在商业化赛道上高歌猛进,Kinde 试图用「零代码」吸引非技术用户。但真正引起我注意的,是一个 2024 年才开源的项目——Better Auth。
2026 年 8 月,Better Auth 官网顶部出现了一行醒目的横幅:「Better Auth is joining Vercel」。这不仅仅是又一个开源项目被大厂收购的故事。它标志着 Web 认证领域一个新时代的开端:认证不再是应用的附属功能,而是基础设施的核心组件。
本文将从架构设计、插件系统、会话管理、数据库集成、安全模型等多个维度,深度拆解 Better Auth 的技术实现,分析它为何能在短短两年内从零成长为 TypeScript 认证领域的标杆项目。
一、为什么需要又一个认证库?
在深入技术细节之前,我们需要回答一个根本问题:Better Auth 到底解决了什么问题?
1.1 认证领域的三大痛点
痛点一:框架绑定
NextAuth.js(Auth.js)是目前最流行的 JavaScript 认证库,但它有一个致命的缺陷——深度绑定 Next.js。虽然项目后来改名为 Auth.js 并尝试支持更多框架,但它的核心设计仍然围绕 Next.js 的 API Routes 和 Server Components 展开。如果你用的是 Hono、Elysia、Solid Start 或者纯 Express,Auth.js 的体验会大打折扣。
痛点二:功能碎片化
Lucia 的设计哲学是「给你最小的核心,其余自己组装」。这在理念上很优雅,但在实践中意味着:你需要自己实现 2FA、自己集成 Passkey、自己处理多租户、自己写 rate limiter。每个项目都在重复造轮子。
痛点三:供应商锁定
Clerk、Supabase Auth、Auth0 等托管服务提供了开箱即用的体验,但代价是你的用户数据存储在第三方服务器上。一旦供应商涨价、停服或者改变策略,迁移成本极高。
1.2 Better Auth 的定位
Better Auth 的核心设计目标是:
- 框架无关:同一套代码可以在 Next.js、Nuxt、SvelteKit、Solid Start、Hono、Express、Cloudflare Workers 等任何支持 Web Standard Request/Response 的环境中运行
- 功能完备:2FA、Passkey、多租户、多会话、Rate Limiting、组织管理等企业级功能开箱即用
- 无锁定:用户数据存储在你自己的数据库中,支持 SQLite、PostgreSQL、MySQL、MongoDB 等主流数据库
- 可扩展:通过插件系统,你可以在不 fork 代码的情况下添加任何自定义功能
用一句话总结:Better Auth 想要做的是「认证领域的 Drizzle ORM」——给你足够的底层控制权,同时提供足够好的默认值。
二、架构设计:Server-Client 分离的哲学
Better Auth 的架构设计可以用一个词概括:分离。
2.1 核心架构
┌─────────────────────────────────────────────────────┐
│ Application │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Auth Client │ HTTP │ Auth Server │ │
│ │ (Frontend) │ ──────► │ (Backend) │ │
│ │ │ │ │ │
│ │ - signIn() │ │ - betterAuth() │ │
│ │ - signUp() │ │ - betterAuthClient() │ │
│ │ - useSession()│ │ - Session Management │ │
│ │ │ │ - Plugin System │ │
│ └──────────────┘ └──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Database │ │
│ │ (SQLite/PG/MySQL)│ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────┘
Better Auth 将认证拆分为两个独立的部分:
Auth Server:处理所有认证逻辑的核心引擎。它是一个独立的模块,可以嵌入到任何后端框架中,也可以作为独立的认证服务部署。
Auth Client:前端 SDK,提供 signIn()、signUp()、useSession() 等 API。针对不同框架提供专用的客户端实现(better-auth/react、better-auth/vue、better-auth/svelte 等)。
这种分离带来的好处是显而易见的:
- 前后端可以独立部署:Auth Server 可以运行在独立的服务器上,前端应用通过 HTTP 与之通信
- 同一个 Auth Server 可以服务多个前端:Web 应用、移动端、桌面端共用一套认证基础设施
- 测试更简单:你可以单独测试 Auth Server 的逻辑,而不必启动整个前端应用
2.2 Server 端实现
Better Auth 的 Server 端配置非常简洁:
import { betterAuth } from "better-auth";
export const auth = betterAuth({
// 数据库配置
database: new Database("./sqlite.db"),
// 认证方法
emailAndPassword: {
enabled: true,
},
// 社交登录
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},
// 插件
plugins: [
twoFactor(),
passkey(),
organization(),
],
// 会话配置
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 天
updateAge: 60 * 60 * 24, // 每天更新
},
});
注意这里的几个关键设计决策:
数据库是必填的:Better Auth 不像某些库那样提供「无状态模式」作为默认选项。它坚持认为认证必须有持久化存储,这是安全的基本要求。
插件是配置的一部分:2FA、Passkey、Organization 等功能通过
plugins数组配置,而不是通过不同的包或模块引入。会话管理是一等公民:
session配置项直接暴露在顶层,而不是隐藏在某个子模块中。
2.3 挂载到 Web 框架
Better Auth 通过 toXxxHandler 辅助函数挂载到各种 Web 框架。以下是几个典型示例:
Next.js App Router:
// app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { POST, GET } = toNextJsHandler(auth);
Hono:
import { Hono } from "hono";
import { auth } from "@/lib/auth";
const app = new Hono();
app.on(["POST", "GET"], "/api/auth/**", (c) => {
return auth.handler(c.req.raw);
});
export default app;
Cloudflare Workers:
import { auth } from "@/lib/auth";
export default {
async fetch(request: Request): Promise<Response> {
return auth.handler(request);
},
};
Express:
import express from "express";
import { auth } from "@/lib/auth";
const app = express();
app.all("/api/auth/*", async (req, res) => {
const response = await auth.handler(new Request(req.url, {
method: req.method,
headers: req.headers,
body: ["GET", "HEAD"].includes(req.method) ? undefined : req.body,
}));
res.status(response.status);
response.headers.forEach((value, key) => res.setHeader(key, value));
res.send(await response.text());
});
这种设计的巧妙之处在于:Better Auth 不依赖任何特定框架的内部 API(如 Next.js 的 cookies()、headers()),而是使用 Web Standard 的 Request 和 Response 对象。 这意味着只要你的框架支持这些标准接口,Better Auth 就能工作。
2.4 Client 端实现
Client 端的使用同样简洁:
// lib/auth-client.ts
import { createAuthClient } from "better-auth/react";
export const authClient = createAuthClient({
baseURL: "http://localhost:3000", // 可选,同域时不需要
});
// 导出常用方法
export const { signIn, signUp, useSession, signOut } = authClient;
在 React 组件中使用:
function Profile() {
const { data: session, isPending } = authClient.useSession();
if (isPending) return <div>Loading...</div>;
if (!session) return <div>Not logged in</div>;
return (
<div>
<p>Welcome, {session.user.name}!</p>
<button onClick={() => signOut()}>Sign Out</button>
</div>
);
}
三、插件系统:认证的乐高积木
Better Auth 最具野心的设计是它的插件系统。与 Auth.js 的 Provider 模式不同,Better Auth 的插件可以修改认证流程的几乎每一个环节。
3.1 内置插件
Better Auth 提供了丰富的内置插件:
| 插件 | 功能 | 说明 |
|---|---|---|
twoFactor() | 双因素认证 | 支持 TOTP(Google Authenticator 等) |
passkey() | Passkey/WebAuthn | 无密码认证,支持生物识别 |
organization() | 组织管理 | 多租户、角色、权限 |
magicLink() | 魔法链接 | 邮箱一键登录 |
username() | 用户名登录 | 除邮箱外支持用户名 |
adminPanel() | 管理面板 | 内置用户管理 UI |
customSession() | 自定义会话 | 扩展会话数据 |
bearer() | Bearer Token | API 认证支持 |
jwt() | JWT 会话 | 无数据库的会话方案 |
3.2 插件实战:Two-Factor Authentication
让我们以 Two-Factor Authentication(2FA)插件为例,看看 Better Auth 的插件是如何工作的。
配置 2FA 插件:
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";
export const auth = betterAuth({
// ... 其他配置
plugins: [
twoFactor({
issuer: "MyApp", // TOTP 发行者名称
totpOptions: {
period: 30, // 30 秒刷新周期
digits: 6, // 6 位验证码
},
}),
],
});
前端启用 2FA:
// 启用 2FA
const { data } = await authClient.twoFactor.enable({
password: userPassword, // 需要验证密码
});
// data.totpUri 可以生成二维码
// data.backupCodes 用于紧急恢复
验证 2FA:
// 登录时验证 2FA
const { data, error } = await authClient.signIn.email({
email: "user@example.com",
password: "password",
});
if (data?.twoFactorRedirect) {
// 需要 2FA 验证
const { error } = await authClient.twoFactor.verify({
code: "123456", // TOTP 验证码
});
}
3.3 插件实战:Passkey
Passkey(通行密钥)是 FIDO2 标准的实现,允许用户使用生物识别(指纹、面容)或硬件密钥进行无密码登录。
import { betterAuth } from "better-auth";
import { passkey } from "better-auth/plugins";
export const auth = betterAuth({
// ... 其他配置
plugins: [
passkey({
rpName: "My Application",
rpId: "myapp.com", // 你的域名
origin: "https://myapp.com", // 完整 Origin
}),
],
});
前端注册 Passkey:
// 注册新 Passkey
const { data, error } = await authClient.passkey.register({
name: "My MacBook Pro",
});
// data 包含 WebAuthn 注册所需的所有信息
// 浏览器会弹出生物识别验证
前端使用 Passkey 登录:
// 使用 Passkey 登录
const { data, error } = await authClient.passkey.signIn();
// 浏览器弹出生物识别验证
// 验证通过后自动登录
3.4 编写自定义插件
Better Auth 的插件系统是完全开放的。你可以编写自己的插件来扩展任何功能:
import type { BetterAuthPlugin } from "better-auth";
export const myCustomPlugin = (): BetterAuthPlugin => {
return {
name: "custom-feature",
// 添加新的 API 端点
endpoints: {
customEndpoint: {
path: "/custom/action",
method: "POST",
handler: async (request) => {
// 自定义逻辑
const body = await request.json();
// ...
return {
status: 200,
body: { success: true },
};
},
},
},
// 扩展数据库 Schema
schema: {
customTable: {
fields: {
id: "string",
userId: "string",
data: "string",
createdAt: "date",
},
},
},
// 修改认证流程
hooks: {
before: async (request, next) => {
// 在请求处理前执行
console.log("Before:", request.url);
return next(request);
},
after: async (request, response, next) => {
// 在请求处理后执行
console.log("After:", response.status);
return next(response);
},
},
};
};
这种插件设计的优雅之处在于:它不是通过继承或 mixin 来扩展功能,而是通过组合。 每个插件都是一个独立的模块,可以自由地添加端点、修改 Schema、挂载 Hook,而不会影响其他插件。
四、会话管理:比你想象的更复杂
会话管理是认证系统中最容易被忽视,也最容易出错的部分。Better Auth 在这方面做了大量工作。
4.1 会话表结构
Better Auth 的会话表包含以下字段:
CREATE TABLE session (
id TEXT PRIMARY KEY,
token TEXT UNIQUE NOT NULL,
userId TEXT NOT NULL,
expiresAt TIMESTAMP NOT NULL,
ipAddress TEXT,
userAgent TEXT,
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
几个关键设计:
- token:会话令牌,同时也是 Cookie 的值。它是不透明的随机字符串,不是 JWT
- expiresAt:过期时间,支持自动续期
- ipAddress / userAgent:用于安全审计和异常检测
4.2 会话过期与续期
Better Auth 的会话续期机制是这样的:
export const auth = betterAuth({
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 天后过期
updateAge: 60 * 60 * 24, // 每 24 小时续期一次
},
});
当用户发起请求时,如果会话的「上次更新时间」超过了 updateAge,Better Auth 会自动将 expiresAt 更新为「当前时间 + expiresIn」。这意味着:
- 用户只要在 7 天内有活动,会话就会持续有效
- 如果用户 7 天没有任何请求,会话过期
你也可以禁用自动续期:
session: {
disableSessionRefresh: true, // 会话一旦创建就不再续期
}
4.3 Cookie 缓存策略
每次请求都查询数据库来验证会话,对于高并发应用来说是不可接受的。Better Auth 提供了 Cookie 缓存来解决这个问题:
session: {
cookieCache: {
enabled: true,
maxAge: 5 * 60, // 缓存 5 分钟
strategy: "compact", // 或 "jwt" 或 "jwe"
},
},
Better Auth 支持三种缓存策略:
| 策略 | 大小 | 安全性 | 可读 | 可互操作 | 适用场景 |
|---|---|---|---|---|---|
compact | 最小 | 良好(签名) | 是 | 否 | 性能优先,内部使用 |
jwt | 中等 | 良好(签名) | 是 | 是 | 需要 JWT 兼容 |
jwe | 最大 | 最佳(加密) | 否 | 是 | 敏感数据,最高安全 |
compact 策略使用 base64url 编码 + HMAC-SHA256 签名,是最紧凑的格式。jwt 策略遵循 JWT 标准,可以被第三方工具验证。jwe 策略使用 AES 加密,数据完全不可读,安全性最高。
4.4 会话安全性
Better Auth 在会话安全方面做了很多细致的工作:
Session Freshness(会话新鲜度):
某些敏感操作(如修改密码、绑定新设备)需要「新鲜」的会话。一个会话被认为是「新鲜的」,如果它的创建时间在 freshAge 之内:
session: {
freshAge: 60 * 5, // 5 分钟内的会话才算新鲜
}
密码修改时吊销会话:
// 修改密码时吊销所有其他会话
await authClient.changePassword({
newPassword: "new-password",
currentPassword: "old-password",
revokeOtherSessions: true, // 关键!
});
读写分离支持:
对于使用读写分离数据库架构的应用,Better Auth 提供了 deferSessionRefresh 选项:
session: {
deferSessionRefresh: true,
}
启用后,GET /get-session 变为只读操作(不会触发数据库写入),会话续期会延迟到下一次 POST 请求时执行。
五、数据库集成:从 SQLite 到 MongoDB
Better Auth 支持多种数据库和 ORM,这是它「无供应商锁定」承诺的核心。
5.1 内置适配器
Better Auth 内置了以下数据库适配器:
- Kysely(默认):SQL 查询构建器,支持 SQLite、PostgreSQL、MySQL
- Drizzle ORM:类型安全的 ORM
- Prisma:流行的 Node.js ORM
- MongoDB:文档数据库
使用 Drizzle ORM:
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@/db"; // 你的 Drizzle 实例
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg", // 或 "mysql", "sqlite"
}),
});
使用 Prisma:
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export const auth = betterAuth({
database: prismaAdapter(prisma, {
provider: "postgresql",
}),
});
使用 MongoDB:
import { betterAuth } from "better-auth";
import { mongodbAdapter } from "better-auth/adapters/mongodb";
import { MongoClient } from "mongodb";
const client = new MongoClient(process.env.MONGODB_URI!);
const db = client.db();
export const auth = betterAuth({
database: mongodbAdapter(db),
});
5.2 自动化 Schema 管理
Better Auth 提供了 CLI 工具来管理数据库 Schema:
# 生成迁移文件
npx auth@latest generate
# 直接执行迁移(仅 Kysely)
npx auth@latest migrate
generate 命令会根据你的插件配置,自动生成所需的数据库表结构。如果你使用 Drizzle 或 Prisma,它会生成对应的 ORM Schema 文件;如果你使用 Kysely,它会生成 SQL 迁移文件。
5.3 扩展 Schema
Better Auth 允许你在核心 Schema 上添加自定义字段:
export const auth = betterAuth({
database: drizzleAdapter(db, { provider: "pg" }),
// 扩展用户表
user: {
additionalFields: {
age: {
type: "number",
required: false,
},
bio: {
type: "string",
required: false,
defaultValue: "",
},
},
},
// 扩展会话表
session: {
additionalFields: {
theme: {
type: "string",
required: false,
},
},
},
});
这些自定义字段会自动包含在数据库 Schema 中,并且可以通过 API 进行读写。
六、安全模型:认证不是儿戏
Better Auth 在安全方面投入了大量精力。以下是几个值得关注的安全特性。
6.1 内置 Rate Limiter
Better Auth 内置了 Rate Limiter,可以防止暴力破解和 DDoS 攻击:
export const auth = betterAuth({
rateLimit: {
window: 60, // 60 秒窗口
max: 100, // 每个 IP 最多 100 次请求
customStatusCode: true, // 返回 429 状态码
},
});
你也可以为不同的端点设置不同的限制:
rateLimit: {
window: 60,
max: 100,
customRules: {
"/sign-in/email": {
window: 60,
max: 5, // 登录接口每分钟最多 5 次
},
"/two-factor/verify": {
window: 60,
max: 3, // 2FA 验证每分钟最多 3 次
},
},
},
6.2 密码安全
Better Auth 使用 bcrypt 进行密码哈希,并且提供了密码强度验证:
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
password: {
hash: async (password) => {
// 自定义哈希函数
return await bcrypt.hash(password, 12);
},
verify: async ({ password, hash }) => {
// 自定义验证函数
return await bcrypt.compare(password, hash);
},
},
// 密码策略
requireEmailVerification: true,
},
});
6.3 CSRF 防护
Better Auth 默认启用 CSRF 防护。它通过验证 Origin 和 Referer 头来防止跨站请求伪造攻击。
6.4 会话绑定
每个会话都会记录创建时的 IP 地址和 User Agent。如果检测到异常(如 IP 突然变化),可以自动吊销会话。
七、AI 友好:面向 Agent 的认证
Better Auth 在 AI 友好性方面做了很多前瞻性的工作,这可能是它被 Vercel 看中的原因之一。
7.1 LLMs.txt
Better Auth 提供了 llms.txt 文件,让 AI 模型可以更好地理解其 API 和用法:
curl https://www.better-auth.com/llms.txt
7.2 MCP Server
Better Auth 提供了官方的 MCP(Model Context Protocol)服务器,让 AI 编码助手可以直接访问其文档:
{
"mcpServers": {
"better-auth": {
"url": "https://mcp.better-auth.com/mcp"
}
}
}
7.3 Agent Skills
Better Auth 提供了专门的 Agent Skills,让 Claude Code、Cursor 等 AI 编码助手可以遵循其最佳实践:
# 安装 Better Auth Skills
npx better-auth-skills install
这意味着当你用 AI 助手编写认证相关代码时,它会自动遵循 Better Auth 的约定和模式,而不是生成不兼容的代码。
八、与竞品的深度对比
8.1 Better Auth vs Auth.js(NextAuth)
| 特性 | Better Auth | Auth.js |
|---|---|---|
| 框架支持 | 任意框架 | 主要 Next.js |
| 2FA | ✅ 内置 | ❌ 需要第三方 |
| Passkey | ✅ 内置 | ❌ 不支持 |
| 多租户 | ✅ 内置 | ❌ 不支持 |
| Rate Limiting | ✅ 内置 | ❌ 需要中间件 |
| 插件系统 | ✅ 完整 | ❌ 有限 |
| 数据库支持 | SQLite/PG/MySQL/MongoDB | 主要 Prisma |
| Bundle Size | ~14KB | ~20KB |
8.2 Better Auth vs Clerk
| 特性 | Better Auth | Clerk |
|---|---|---|
| 部署方式 | 自托管 | 托管服务 |
| 数据所有权 | ✅ 你的数据库 | ❌ Clerk 服务器 |
| 定价 | 免费(开源) | 按用户计费 |
| 企业功能 | 通过插件 | 内置 |
| UI 组件 | 可选 | 丰富 |
| 多租户 | ✅ 插件 | ✅ 内置 |
8.3 Better Auth vs Supabase Auth
| 特性 | Better Auth | Supabase Auth |
|---|---|---|
| 依赖 | 无 | 需要 Supabase |
| 数据库 | 任意 | 仅 PostgreSQL |
| 实时同步 | ❌ | ✅ |
| Row Level Security | ❌ | ✅ |
| 自定义 | 完全 | 有限 |
九、Vercel 收购:意味着什么?
Better Auth 加入 Vercel,这个消息背后有几个值得关注的信号:
9.1 Vercel 的全栈野心
Vercel 已经有了 Next.js 作为前端框架,有了 Turbopack 作为构建工具,有了 Edge Runtime 作为运行时。现在它需要一个认证层来完成全栈拼图。
Auth.js 虽然名义上属于 Next.js 生态,但它的维护状态一直不稳定。Better Auth 的加入可以填补这个空白。
9.2 开源 + 商业化
Vercel 的商业模式一直是「开源核心 + 商业增值」。Better Auth 很可能会遵循类似的路径:
- 核心库保持开源
- 提供托管认证服务(类似 Vercel Auth)
- 提供企业级功能和支持
9.3 对社区的影响
对于开发者来说,这既是好消息也是坏消息:
好消息:Better Auth 会获得更多的资源和维护投入,长期稳定性更有保障。
坏消息:Vercel 可能会在某些功能上设置付费门槛,或者将某些特性深度绑定到 Vercel 平台。
但无论如何,Better Auth 的核心代码仍然在 MIT 许可证下开源,社区 fork 的权利不会消失。
十、实战:从零搭建完整的认证系统
让我们通过一个完整的示例,展示如何用 Better Auth 搭建一个生产级的认证系统。
10.1 项目初始化
# 创建项目
mkdir my-auth-app && cd my-auth-app
npm init -y
# 安装依赖
npm install better-auth better-sqlite3
npm install -D typescript @types/node
# 初始化 TypeScript
npx tsc --init
10.2 配置 Auth Server
// lib/auth.ts
import { betterAuth } from "better-auth";
import { twoFactor, passkey, organization } from "better-auth/plugins";
import Database from "better-sqlite3";
export const auth = betterAuth({
database: new Database("./auth.db"),
emailAndPassword: {
enabled: true,
requireEmailVerification: false, // 开发环境关闭
},
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
plugins: [
twoFactor({
issuer: "MyApp",
}),
passkey({
rpName: "My App",
rpId: "localhost",
origin: "http://localhost:3000",
}),
organization(),
],
session: {
expiresIn: 60 * 60 * 24 * 7,
cookieCache: {
enabled: true,
maxAge: 5 * 60,
},
},
rateLimit: {
window: 60,
max: 100,
},
});
10.3 挂载到 Express
// server.ts
import express from "express";
import cors from "cors";
import { auth } from "./lib/auth";
const app = express();
app.use(cors({
origin: "http://localhost:3000",
credentials: true,
}));
app.use(express.json());
// 挂载 Better Auth 路由
app.all("/api/auth/*", async (req, res) => {
const url = new URL(req.url, `http://${req.headers.host}`);
const headers = new Headers();
Object.entries(req.headers).forEach(([key, value]) => {
if (value) headers.set(key, Array.isArray(value) ? value[0] : value);
});
const request = new Request(url.toString(), {
method: req.method,
headers,
body: ["GET", "HEAD"].includes(req.method) ? undefined : JSON.stringify(req.body),
});
const response = await auth.handler(request);
res.status(response.status);
response.headers.forEach((value, key) => {
res.setHeader(key, value);
});
const body = await response.text();
res.send(body);
});
// 受保护的 API 示例
app.get("/api/profile", async (req, res) => {
const session = await auth.api.getSession({
headers: req.headers as any,
});
if (!session) {
return res.status(401).json({ error: "Unauthorized" });
}
res.json({
user: session.user,
session: session.session,
});
});
app.listen(3000, () => {
console.log("Auth server running on http://localhost:3000");
});
10.4 前端使用
// lib/auth-client.ts
import { createAuthClient } from "better-auth/react";
export const authClient = createAuthClient({
baseURL: "http://localhost:3000",
});
export const { signIn, signUp, signOut, useSession } = authClient;
// components/LoginForm.tsx
"use client";
import { useState } from "react";
import { signIn, signUp } from "@/lib/auth-client";
export function LoginForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [isSignUp, setIsSignUp] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (isSignUp) {
const { error } = await signUp.email({
email,
password,
name: email.split("@")[0],
});
if (error) console.error(error);
} else {
const { error } = await signIn.email({
email,
password,
});
if (error) console.error(error);
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
<button type="submit">
{isSignUp ? "Sign Up" : "Sign In"}
</button>
<button type="button" onClick={() => setIsSignUp(!isSignUp)}>
{isSignUp ? "Already have an account? Sign In" : "Don't have an account? Sign Up"}
</button>
</form>
);
}
十一、性能优化与生产部署
11.1 Bundle Size 优化
如果你使用 Drizzle、Prisma 或 MongoDB 适配器,建议从 better-auth/minimal 导入以减小 bundle size:
// 优化导入
import { betterAuth } from "better-auth/minimal";
11.2 数据库连接池
在生产环境中,确保配置合适的数据库连接池:
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20,
idleTimeoutMillis: 30000,
});
export const auth = betterAuth({
database: drizzleAdapter(pool, { provider: "pg" }),
});
11.3 Redis 会话存储
对于分布式部署,可以将会话存储在 Redis 中:
export const auth = betterAuth({
session: {
storeSessionInCookie: false, // 不使用 Cookie 存储会话数据
},
secondaryStorage: {
// 自定义二级存储
get: async (key) => {
return await redis.get(key);
},
set: async (key, value, ttl) => {
await redis.setex(key, ttl, value);
},
delete: async (key) => {
await redis.del(key);
},
},
});
十二、总结与展望
Better Auth 在短短两年内从零成长为 TypeScript 认证领域的标杆项目,这并非偶然。它的成功可以归结为几个关键因素:
正确的时机:Lucia 停更留下的真空,Auth.js 的不稳定,托管服务的锁定问题——Better Auth 精准地填补了这些空白。
正确的设计:框架无关、功能完备、可扩展——这三个看似矛盾的目标,Better Auth 通过精心的架构设计实现了统一。
正确的社区策略:AI 友好(LLMs.txt、MCP、Skills)、清晰的文档、活跃的社区——这些都降低了采用门槛。
正确的商业化路径:加入 Vercel 既获得了资源保障,又保持了开源承诺——这在开源商业化越来越难的今天,是一个值得借鉴的案例。
展望未来,我认为 Better Auth 有几个值得关注的方向:
- Edge 部署:随着 Vercel Edge Runtime 的成熟,Better Auth 可能会提供原生的 Edge 支持
- WebAuthn 2.0:Passkey 的普及将推动 Better Auth 在无密码认证领域的进一步发展
- AI 原生认证:随着 AI Agent 的兴起,认证系统需要支持更多的机器对机器认证场景
- 多租户 SaaS:Organization 插件的持续演进将使 Better Auth 成为 SaaS 应用的首选认证方案
对于开发者来说,如果你正在寻找一个:
- 不绑定特定框架的认证方案
- 不需要为每个用户付费的开源方案
- 企业级功能开箱即用的完整方案
Better Auth 值得认真考虑。它的代码库清晰、文档完善、社区活跃,是一个难得的「既好用又有深度」的开源项目。
项目地址:https://github.com/better-auth/better-auth
官方文档:https://better-auth.com
Vercel 公告:https://better-auth.com(首页横幅)