编程 Better Auth 深度拆解:当开源认证决定「不再做 NextAuth 的替代品」——从 Lucia 停更到 Vercel 收购,一个 TypeScript 全栈认证框架如何用「插件化架构 + 无供应商锁定」重新定义 Web 认证的终极形态

2026-08-06 02:24:09 +0800 CST views 12

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/reactbetter-auth/vuebetter-auth/svelte 等)。

这种分离带来的好处是显而易见的:

  1. 前后端可以独立部署:Auth Server 可以运行在独立的服务器上,前端应用通过 HTTP 与之通信
  2. 同一个 Auth Server 可以服务多个前端:Web 应用、移动端、桌面端共用一套认证基础设施
  3. 测试更简单:你可以单独测试 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,     // 每天更新
  },
});

注意这里的几个关键设计决策:

  1. 数据库是必填的:Better Auth 不像某些库那样提供「无状态模式」作为默认选项。它坚持认为认证必须有持久化存储,这是安全的基本要求。

  2. 插件是配置的一部分:2FA、Passkey、Organization 等功能通过 plugins 数组配置,而不是通过不同的包或模块引入。

  3. 会话管理是一等公民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 的 RequestResponse 对象。 这意味着只要你的框架支持这些标准接口,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 TokenAPI 认证支持
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, // 会话一旦创建就不再续期
}

每次请求都查询数据库来验证会话,对于高并发应用来说是不可接受的。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 防护。它通过验证 OriginReferer 头来防止跨站请求伪造攻击。

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 AuthAuth.js
框架支持任意框架主要 Next.js
2FA✅ 内置❌ 需要第三方
Passkey✅ 内置❌ 不支持
多租户✅ 内置❌ 不支持
Rate Limiting✅ 内置❌ 需要中间件
插件系统✅ 完整❌ 有限
数据库支持SQLite/PG/MySQL/MongoDB主要 Prisma
Bundle Size~14KB~20KB

8.2 Better Auth vs Clerk

特性Better AuthClerk
部署方式自托管托管服务
数据所有权✅ 你的数据库❌ Clerk 服务器
定价免费(开源)按用户计费
企业功能通过插件内置
UI 组件可选丰富
多租户✅ 插件✅ 内置

8.3 Better Auth vs Supabase Auth

特性Better AuthSupabase 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 认证领域的标杆项目,这并非偶然。它的成功可以归结为几个关键因素:

  1. 正确的时机:Lucia 停更留下的真空,Auth.js 的不稳定,托管服务的锁定问题——Better Auth 精准地填补了这些空白。

  2. 正确的设计:框架无关、功能完备、可扩展——这三个看似矛盾的目标,Better Auth 通过精心的架构设计实现了统一。

  3. 正确的社区策略:AI 友好(LLMs.txt、MCP、Skills)、清晰的文档、活跃的社区——这些都降低了采用门槛。

  4. 正确的商业化路径:加入 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(首页横幅)

推荐文章

如何在Vue中处理动态路由?
2024-11-19 06:09:50 +0800 CST
资源文档库
2024-12-07 20:42:49 +0800 CST
Python 获取网络时间和本地时间
2024-11-18 21:53:35 +0800 CST
免费常用API接口分享
2024-11-19 09:25:07 +0800 CST
Linux查看系统配置常用命令
2024-11-17 18:20:42 +0800 CST
CSS 实现金额数字滚动效果
2024-11-19 09:17:15 +0800 CST
Vue3中如何处理路由和导航?
2024-11-18 16:56:14 +0800 CST
SQL常用优化的技巧
2024-11-18 15:56:06 +0800 CST
程序员茄子在线接单