mcpField
B
better-auth

better-auth

github.com/ovachiever安全5654 次安装48 周前更新
代码仓库

关于 better-auth

面向构建 Web 应用认证功能的开发者,提供 better-auth 配置与使用指南,涵盖适配器、框架设置、端点及插件,包括 MCP OAuth 提供方插件。

主要功能

  1. 01Drizzle 与 Kysely 适配器指南
  2. 02特定框架的配置说明
  3. 03认证端点参考
  4. 04组织与管理插件
  5. 05MCP OAuth 提供方插件

适用场景

  1. 01为 D1 数据库配置 better-auth
  2. 02在 TanStack Start 中设置认证
  3. 03添加组织和团队管理
  4. 04为 MCP 客户端提供 OAuth

MCP 相关

MCP 插件可让 better-auth 充当 MCP 客户端的 OAuth 提供方。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ovachiever/droid-tings --skill better-auth

安装后在 Claude Code 中输入/better-auth确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
.claude-plugin/
plugin.json
assets/
auth-flow-diagram.md
references/
cloudflare-worker-drizzle.ts
cloudflare-worker-kysely.ts
database-schema.ts
react-client-hooks.tsx
references/nextjs/
postgres-example.ts
README.md
scripts/
setup-d1-drizzle.sh

详细介绍

better-auth - D1 适配器与错误预防指南

软件包:[email protected](2025 年 11 月 22 日) 破坏性变更:仅支持 ESM(v1.4.0)、多团队表变更(v1.3)、D1 需要 Drizzle/Kysely(无直接适配器)


⚠️ 重要:D1 适配器要求

better-auth 没有 d1Adapter()。你必须使用:

  • Drizzle ORM(推荐):drizzleAdapter(db, { provider: "sqlite" })
  • Kysely:new Kysely({ dialect: new D1Dialect({ database: env.DB }) })

详情请参阅下方问题 #1。


v1.4.0 新增内容(2025 年 11 月 22 日)

主要功能:

  • 无状态会话管理 - 无需数据库存储的会话
  • 仅支持 ESM 的软件包 ⚠️ 破坏性变更:不再支持 CommonJS
  • JWT 密钥轮换 - 自动轮换密钥,增强安全性
  • SCIM 配置 - 企业用户配置协议
  • @standard-schema/spec - 替代用于验证的 ZodType
  • CaptchaFox 集成 - 内置 CAPTCHA 支持
  • 自动检测服务端 IP
  • 基于 Cookie 的账户数据存储
  • 支持多个通行密钥来源
  • RP 发起的注销端点(OIDC)

📚 文档:https://www.better-auth.com/changelogs


v1.3 新增内容(2025 年 7 月)

主要功能:

  • 支持 SAML 2.0 的 SSO - 企业单点登录(已移至单独的 @better-auth/sso 软件包)
  • 多团队支持 ⚠️ 破坏性变更:从成员表中移除 teamId,需要新增 teamMembers 表
  • 附加字段 - 为组织/成员/邀请模型添加自定义字段
  • 性能改进和错误修复

📚 文档:https://www.better-auth.com/blog/1-3


替代方案:Kysely 适配器模式

如果你更喜欢使用 Kysely 而非 Drizzle:

文件:src/auth.ts

import { betterAuth } from "better-auth";
import { Kysely, CamelCasePlugin } from "kysely";
import { D1Dialect } from "kysely-d1";

type Env = {
  DB: D1Database;
  BETTER_AUTH_SECRET: string;
  // ... other env vars
};

export function createAuth(env: Env) {
  return betterAuth({
    secret: env.BETTER_AUTH_SECRET,

    // Kysely with D1Dialect
    database: {
      db: new Kysely({
        dialect: new D1Dialect({
          database: env.DB,
        }),
        plugins: [
          // CRITICAL: Required if using Drizzle schema with snake_case
          new CamelCasePlugin(),
        ],
      }),
      type: "sqlite",
    },

    emailAndPassword: {
      enabled: true,
    },

    // ... other config
  });
}

为什么需要 CamelCasePlugin?

如果你的 Drizzle 架构使用 snake_case 列名(例如 email_verified),而 better-auth 需要的是 camelCase(例如 emailVerified),CamelCasePlugin 会自动在两者之间转换。


框架集成

TanStack Start

⚠️ 重要:TanStack Start 需要 reactStartCookies 插件才能正确处理 Cookie 设置。

import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { reactStartCookies } from "better-auth/react-start";

export const auth = betterAuth({
  database: drizzleAdapter(db, { provider: "sqlite" }),
  plugins: [
    twoFactor(),
    organization(),
    reactStartCookies(), // ⚠️ MUST be LAST plugin
  ],
});

为什么需要它:TanStack Start 使用一种特殊的 Cookie 处理系统。没有此插件,signInEmail() 和 signUpEmail() 等身份验证函数将无法正确设置 Cookie,导致身份验证失败。

重要:reactStartCookies 插件必须是数组中的最后一个插件。

API 路由设置(/src/routes/api/auth/$.ts):

import { auth } from '@/lib/auth'
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/api/auth/$')({
  server: {
    handlers: {
      GET: ({ request }) => auth.handler(request),
      POST: ({ request }) => auth.handler(request),
    },
  },
})

📚 官方文档:https://www.better-auth.com/docs/integrations/tanstack


可用插件(v1.3+)

Better Auth 提供用于高级身份验证功能的插件:

插件导入说明文档
OIDC 提供方better-auth/plugins构建自己的 OpenID Connect 提供方(为其他应用充当 OAuth 提供方)📚
SSObetter-auth/plugins支持 OIDC、OAuth2 和 SAML 2.0 的企业单点登录📚
Stripebetter-auth/plugins支付和订阅管理(自 v1.3+ 起稳定)📚
MCPbetter-auth/plugins为模型上下文协议(MCP)客户端充当 OAuth 提供方📚
Expobetter-auth/expoReact Native/Expo 集成,支持安全的 Cookie 管理📚

API 参考

概览:你可免费获得的功能

调用 auth.handler() 时,better-auth 会自动在 /api/auth/* 下暴露 80 多个可用于生产环境的 REST 端点。每个端点也都可通过 auth.api.* 作为服务端方法使用,以便通过代码调用。

这种双层 API 系统意味着:

  • 客户端(React、Vue、移动应用)可直接调用 HTTP 端点
  • 服务端代码(中间件、后台任务)使用 auth.api.* 方法
  • 零样板代码——无需手动编写身份验证端点

节省时间:从头构建约需 220 小时。使用 better-auth 约需 4–8 小时。减少 97%。


自动生成的 HTTP 端点

使用 auth.handler() 时,所有端点都会自动在 /api/auth/* 下暴露。

核心身份验证端点
端点方法描述
/sign-up/emailPOST使用电子邮件/密码注册
/sign-in/emailPOST使用电子邮件/密码进行身份验证
/sign-outPOST用户登出
/change-passwordPOST更新密码(需要当前密码)
/forget-passwordPOST发起密码重置流程
/reset-passwordPOST使用令牌完成密码重置
/send-verification-emailPOST发送电子邮件验证链接
/verify-emailGET使用令牌验证电子邮件(?token=<token>)
/get-sessionGET获取当前会话
/list-sessionsGET获取用户的所有活动会话
/revoke-sessionPOST结束指定会话
/revoke-other-sessionsPOST结束除当前会话外的所有会话
/revoke-sessionsPOST结束用户的所有会话
/update-userPOST修改用户资料(姓名、图片)
/change-emailPOST更新电子邮件地址
/set-passwordPOST为仅使用 OAuth 的账户添加密码
/delete-userPOST删除用户账户
/list-accountsGET获取已关联的身份验证提供商
/link-socialPOST将 OAuth 提供商关联到账户
/unlink-accountPOST取消关联提供商
社交 OAuth 端点
端点方法描述
/sign-in/socialPOST发起 OAuth 流程(在请求正文中指定提供商)
/callback/:providerGETOAuth 回调处理程序(例如,/callback/google)
/get-access-tokenGET获取提供商访问令牌

OAuth 流程示例:

// Client initiates
await authClient.signIn.social({
  provider: "google",
  callbackURL: "/dashboard",
});

// better-auth handles redirect to Google
// Google redirects back to /api/auth/callback/google
// better-auth creates session automatically

插件端点
双重身份验证(2FA 插件)
import { twoFactor } from "better-auth/plugins";
端点方法描述
/two-factor/enablePOST为用户启用 2FA
/two-factor/disablePOST停用 2FA
/two-factor/get-totp-uriGET获取身份验证器应用的二维码 URI
/two-factor/verify-totpPOST验证身份验证器中的 TOTP 代码
/two-factor/send-otpPOST通过电子邮件发送 OTP
/two-factor/verify-otpPOST验证电子邮件 OTP
/two-factor/generate-backup-codesPOST生成恢复代码
/two-factor/verify-backup-codePOST使用备用代码登录
/two-factor/view-backup-codesGET查看当前备用代码

📚 文档:https://www.better-auth.com/docs/plugins/2fa

组织插件(多租户 SaaS)
import { organization } from "better-auth/plugins";

组织(10 个端点):

EndpointMethodDescription
/organization/createPOST创建组织
/organization/listGET列出用户的组织
/organization/get-fullGET获取完整的组织详情
/organization/updatePUT修改组织
/organization/deleteDELETE移除组织
/organization/check-slugGET检查 slug 是否可用
/organization/set-activePOST设置当前组织上下文

成员(8 个端点):

EndpointMethodDescription
/organization/list-membersGET获取组织成员
/organization/add-memberPOST直接添加成员
/organization/remove-memberDELETE移除成员
/organization/update-member-rolePUT更改成员角色
/organization/get-active-memberGET获取当前成员信息
/organization/leavePOST离开组织

邀请(7 个端点):

EndpointMethodDescription
/organization/invite-memberPOST发送邀请邮件
/organization/accept-invitationPOST接受邀请
/organization/reject-invitationPOST拒绝邀请
/organization/cancel-invitationPOST取消待处理的邀请
/organization/get-invitationGET获取邀请详情
/organization/list-invitationsGET列出组织邀请
/organization/list-user-invitationsGET列出用户待处理的邀请

团队(8 个端点):

EndpointMethodDescription
/organization/create-teamPOST在组织内创建团队
/organization/list-teamsGET列出组织团队
/organization/update-teamPUT修改团队
/organization/remove-teamDELETE移除团队
/organization/set-active-teamPOST设置当前团队上下文
/organization/list-team-membersGET列出团队成员
/organization/add-team-memberPOST将成员添加到团队
/organization/remove-team-memberDELETE移除团队成员

权限与角色(6 个端点):

EndpointMethodDescription
/organization/has-permissionPOST检查用户是否拥有权限
/organization/create-rolePOST创建自定义角色
/organization/delete-roleDELETE删除自定义角色
/organization/list-rolesGET列出所有角色
/organization/get-roleGET获取角色详情
/organization/update-rolePUT修改角色权限

📚 文档:https://www.better-auth.com/docs/plugins/organization

管理员插件
import { admin } from "better-auth/plugins";
EndpointMethodDescription
/admin/create-userPOST以管理员身份创建用户
/admin/list-usersGET列出所有用户(支持筛选和分页)
/admin/set-rolePOST为用户分配角色
/admin/set-user-passwordPOST更改用户密码
/admin/update-userPUT修改用户详情
/admin/remove-userDELETE删除用户账户
/admin/ban-userPOST封禁用户账户
/admin/unban-userPOST解封用户
/admin/list-user-sessionsGET获取用户的活跃会话
/admin/revoke-user-sessionDELETE结束指定的用户会话
/admin/revoke-user-sessionsDELETE结束用户的所有会话
/admin/impersonate-userPOST开始模拟用户身份
/admin/stop-impersonatingPOST结束模拟会话

📚 文档:https://www.better-auth.com/docs/plugins/admin

其他插件端点

Passkey 插件(5 个端点)- 文档:

  • /passkey/add、/sign-in/passkey、/passkey/list、/passkey/delete、/passkey/update

Magic Link 插件(2 个端点)- 文档:

  • /sign-in/magic-link、/magic-link/verify

Username 插件(2 个端点)- 文档:

  • /sign-in/username、/username/is-available

Phone Number 插件(5 个端点)- 文档:

  • /sign-in/phone-number、/phone-number/send-otp、/phone-number/verify、/phone-number/request-password-reset、/phone-number/reset-password

Email OTP 插件(6 个端点)- 文档:

  • /email-otp/send-verification-otp、/email-otp/check-verification-otp、/sign-in/email-otp、/email-otp/verify-email、/forget-password/email-otp、/email-otp/reset-password

Anonymous 插件(1 个端点)- 文档:

  • /sign-in/anonymous

JWT 插件(2 个端点)- 文档:

  • /token(获取 JWT)、/jwks(用于验证的公钥)

OpenAPI 插件(2 个端点)- 文档:

  • /reference(使用 Scalar UI 的交互式 API 文档)
  • /generate-openapi-schema(以 JSON 格式获取 OpenAPI 规范)

服务端 API 方法(auth.api.*)

每个 HTTP 端点都有对应的服务端方法。可用于:

  • 服务端中间件(保护路由)
  • 后台任务(用户清理、通知)
  • 管理员操作(批量管理用户)
  • 自定义身份验证流程(以编程方式创建会话)
核心 API 方法
// Authentication
await auth.api.signUpEmail({
  body: { email, password, name },
  headers: request.headers,
});

await auth.api.signInEmail({
  body: { email, password, rememberMe: true },
  headers: request.headers,
});

await auth.api.signOut({ headers: request.headers });

// Session Management
const session = await auth.api.getSession({ headers: request.headers });

await auth.api.listSessions({ headers: request.headers });

await auth.api.revokeSession({
  body: { token: "session_token_here" },
  headers: request.headers,
});

// User Management
await auth.api.updateUser({
  body: { name: "New Name", image: "https://..." },
  headers: request.headers,
});

await auth.api.changeEmail({
  body: { newEmail: "[email protected]" },
  headers: request.headers,
});

await auth.api.deleteUser({
  body: { password: "current_password" },
  headers: request.headers,
});

// Account Linking
await auth.api.linkSocialAccount({
  body: { provider: "google" },
  headers: request.headers,
});

await auth.api.unlinkAccount({
  body: { providerId: "google", accountId: "google_123" },
  headers: request.headers,
});
插件 API 方法

2FA 插件:

// Enable 2FA
const { totpUri, backupCodes } = await auth.api.enableTwoFactor({
  body: { issuer: "MyApp" },
  headers: request.headers,
});

// Verify TOTP code
await auth.api.verifyTOTP({
  body: { code: "123456", trustDevice: true },
  headers: request.headers,
});

// Generate backup codes
const { backupCodes } = await auth.api.generateBackupCodes({
  headers: request.headers,
});

Organization 插件:

// Create organization
const org = await auth.api.createOrganization({
  body: { name: "Acme Corp", slug: "acme" },
  headers: request.headers,
});

// Add member
await auth.api.addMember({
  body: {
    userId: "user_123",
    role: "admin",
    organizationId: org.id,
  },
  headers: request.headers,
});

// Check permissions
const hasPermission = await auth.api.hasPermission({
  body: {
    organizationId: org.id,
    permission: "users:delete",
  },
  headers: request.headers,
});

Admin 插件:

// List users with pagination
const users = await auth.api.listUsers({
  query: {
    search: "john",
    limit: 10,
    offset: 0,
    sortBy: "createdAt",
    sortOrder: "desc",
  },
  headers: request.headers,
});

// Ban user
await auth.api.banUser({
  body: {
    userId: "user_123",
    reason: "Violation of ToS",
    expiresAt: new Date("2025-12-31"),
  },
  headers: request.headers,
});

// Impersonate user (for admin support)
const impersonationSession = await auth.api.impersonateUser({
  body: {
    userId: "user_123",
    expiresIn: 3600, // 1 hour
  },
  headers: request.headers,
});

何时使用哪种方式
使用场景使用 HTTP 端点使用 auth.api.* 方法
客户端身份验证✅ 是❌ 否
服务端中间件❌ 否✅ 是
后台任务❌ 否✅ 是
管理员仪表板✅ 是(来自客户端)✅ 是(来自服务端)
自定义身份验证流程❌ 否✅ 是
移动应用✅ 是❌ 否
API 路由✅ 是(代理到处理程序)✅ 是(直接调用)

示例:受保护路由中间件

import { Hono } from "hono";
import { createAuth } from "./auth";
import { createDatabase } from "./db";

const app = new Hono<{ Bindings: Env }>();

// Middleware using server-side API
app.use("/api/protected/*", async (c, next) => {
  const db = createDatabase(c.env.DB);
  const auth = createAuth(db, c.env);

  // Use server-side method
  const session = await auth.api.getSession({
    headers: c.req.raw.headers,
  });

  if (!session) {
    return c.json({ error: "Unauthorized" }, 401);
  }

  // Attach to context
  c.set("user", session.user);
  c.set("session", session.session);

  await next();
});

// Protected route
app.get("/api/protected/profile", async (c) => {
  const user = c.get("user");
  return c.json({ user });
});

查找可用端点

使用 OpenAPI 插件查看配置中的所有端点:

import { betterAuth } from "better-auth";
import { openAPI } from "better-auth/plugins";

export const auth = betterAuth({
  database: /* ... */,
  plugins: [
    openAPI(), // Adds /api/auth/reference endpoint
  ],
});

交互式文档:访问 http://localhost:8787/api/auth/reference

这里会显示 Scalar UI,其中包含:

  • ✅ 按功能分组的所有可用端点
  • ✅ 带类型的请求/响应架构
  • ✅ 试用功能(在浏览器中测试端点)
  • ✅ 身份验证要求
  • ✅ 多种语言的代码示例

以编程方式访问:

const schema = await auth.api.generateOpenAPISchema();
console.log(JSON.stringify(schema, null, 2));
// Returns full OpenAPI 3.0 spec

量化节省的时间

从头开始构建(手动实现):

  • 核心身份验证端点(注册、登录、OAuth、会话):40 小时
  • 电子邮件验证和密码重置:10 小时
  • 2FA 系统(TOTP、备用代码、电子邮件 OTP):20 小时
  • 组织(团队、邀请、RBAC):60 小时
  • 管理面板(用户管理、模拟用户):30 小时
  • 测试和调试:50 小时
  • 安全加固:20 小时

手动投入总计:约 220 小时(全职 5.5 周)

使用 better-auth:

  • 初始设置:2-4 小时
  • 自定义和样式调整:2-4 小时

使用 better-auth 所需总计:4-8 小时

节省:约 97% 的开发时间


关键要点

better-auth 提供 80 多个可用于生产环境的端点,涵盖:

  • ✅ 核心身份验证(20 个端点)
  • ✅ 双重身份验证与无密码登录(15 个端点)
  • ✅ 组织与团队(35 个端点)
  • ✅ 管理与用户管理(15 个端点)
  • ✅ 社交 OAuth(自动配置回调)
  • ✅ OpenAPI 文档(交互式界面)

你无需编写任何端点代码。 只需配置功能并调用 auth.handler()。


已知问题与解决方案

问题 1:"d1Adapter is not exported" 错误

问题:代码显示 import { d1Adapter } from 'better-auth/adapters/d1',但该导出不存在。

症状:出现 TypeScript 错误或有关缺少导出的运行时错误。

解决方案:改用 Drizzle 或 Kysely:

// ❌ WRONG - This doesn't exist
import { d1Adapter } from 'better-auth/adapters/d1'
database: d1Adapter(env.DB)

// ✅ CORRECT - Use Drizzle
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
import { drizzle } from 'drizzle-orm/d1'
const db = drizzle(env.DB, { schema })
database: drizzleAdapter(db, { provider: "sqlite" })

// ✅ CORRECT - Use Kysely
import { Kysely } from 'kysely'
import { D1Dialect } from 'kysely-d1'
database: {
  db: new Kysely({ dialect: new D1Dialect({ database: env.DB }) }),
  type: "sqlite"
}

来源:已通过 4 个在生产环境中使用 better-auth + D1 的代码库验证


问题 2:架构生成失败

问题:npx better-auth migrate 未能创建兼容 D1 的架构。

症状:迁移 SQL 语法错误,或无法与 D1 配合使用。

解决方案:使用 Drizzle Kit 生成迁移:

# Generate migration from Drizzle schema
npx drizzle-kit generate

# Apply to D1
wrangler d1 migrations apply my-app-db --remote

原因:Drizzle Kit 会生成兼容 SQLite 的 SQL,可与 D1 配合使用。


问题 3:"CamelCase" 与 "snake_case" 列名不匹配

问题:数据库中有 email_verified,但 better-auth 预期的是 emailVerified。

症状:会话读取失败,用户数据缺少字段。

解决方案:在 Kysely 中使用 CamelCasePlugin,或正确配置 Drizzle:

使用 Kysely:

import { CamelCasePlugin } from "kysely";

new Kysely({
  dialect: new D1Dialect({ database: env.DB }),
  plugins: [new CamelCasePlugin()], // Converts between naming conventions
})

使用 Drizzle:从一开始就使用 camelCase 定义架构(如示例所示)。


问题 4:D1 最终一致性

问题:写入后立即读取会话时返回过时数据。

症状:用户登录后,下一次请求中的 getSession() 返回 null。

解决方案:使用 Cloudflare KV 存储会话(强一致性):

import { betterAuth } from "better-auth";

export function createAuth(db: Database, env: Env) {
  return betterAuth({
    database: drizzleAdapter(db, { provider: "sqlite" }),
    session: {
      storage: {
        get: async (sessionId) => {
          const session = await env.SESSIONS_KV.get(sessionId);
          return session ? JSON.parse(session) : null;
        },
        set: async (sessionId, session, ttl) => {
          await env.SESSIONS_KV.put(sessionId, JSON.stringify(session), {
            expirationTtl: ttl,
          });
        },
        delete: async (sessionId) => {
          await env.SESSIONS_KV.delete(sessionId);
        },
      },
    },
  });
}

添加到 wrangler.toml:

[[kv_namespaces]]
binding = "SESSIONS_KV"
id = "your-kv-namespace-id"

问题 5:SPA 应用的 CORS 错误

问题:身份验证 API 与前端位于不同源时出现 CORS 错误。

症状:浏览器控制台中出现 Access-Control-Allow-Origin 错误。

解决方案:在 Worker 中配置 CORS 标头:

import { cors } from "hono/cors";

app.use(
  "/api/auth/*",
  cors({
    origin: ["https://yourdomain.com", "http://localhost:3000"],
    credentials: true, // Allow cookies
    allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
  })
);

问题 6:OAuth 重定向 URI 不匹配

问题:社交登录失败,并出现 "redirect_uri_mismatch" 错误。

症状:Google/GitHub OAuth 在用户授权后返回错误。

解决方案:确保 OAuth 提供商设置中的 URI 完全匹配:

Provider setting: https://yourdomain.com/api/auth/callback/google
better-auth URL:  https://yourdomain.com/api/auth/callback/google

❌ Wrong: http vs https, trailing slash, subdomain mismatch
✅ Right: Exact character-for-character match

检查 better-auth 回调 URL:

// It's always: {baseURL}/api/auth/callback/{provider}
const callbackURL = `${env.BETTER_AUTH_URL}/api/auth/callback/google`;
console.log("Configure this URL in Google Console:", callbackURL);

问题 7:缺少依赖项

问题:出现有关缺少软件包的 TypeScript 错误或运行时错误。

症状:出现 Cannot find module 'drizzle-orm' 或类似错误。

解决方案:安装所有必需的软件包:

Drizzle 方案:

npm install better-auth drizzle-orm drizzle-kit @cloudflare/workers-types

Kysely 方案:

npm install better-auth kysely kysely-d1 @cloudflare/workers-types

问题 8:未发送电子邮件验证邮件

问题:始终收不到电子邮件验证链接。

症状:用户注册后没有收到电子邮件。

解决方案:实现 sendVerificationEmail 处理函数:

export const auth = betterAuth({
  database: /* ... */,
  emailAndPassword: {
    enabled: true,
    requireEmailVerification: true,
  },
  emailVerification: {
    sendVerificationEmail: async ({ user, url }) => {
      // Use your email service (SendGrid, Resend, etc.)
      await sendEmail({
        to: user.email,
        subject: "Verify your email",
        html: `
          <p>Click the link below to verify your email:</p>
          <a href="${url}">Verify Email</a>
        `,
      });
    },
    sendOnSignUp: true,
    autoSignInAfterVerification: true,
    expiresIn: 3600, // 1 hour
  },
});

对于 Cloudflare:使用 Cloudflare Email Routing 或外部服务(Resend、SendGrid)。


问题 9:会话过期过快

问题:会话意外过期,或永不过期。

症状:用户意外退出登录,或退出登录后会话仍然存在。

解决方案:配置会话过期时间:

export const auth = betterAuth({
  database: /* ... */,
  session: {
    expiresIn: 60 * 60 * 24 * 7, // 7 days (in seconds)
    updateAge: 60 * 60 * 24, // Update session every 24 hours
  },
});

问题 10:社交登录提供商未提供用户数据

问题:社交登录成功,但缺少用户数据(姓名、头像)。

症状:通过 Google/GitHub 登录后,session.user.name 为 null。

解决方案:请求更多权限范围:

socialProviders: {
  google: {
    clientId: env.GOOGLE_CLIENT_ID,
    clientSecret: env.GOOGLE_CLIENT_SECRET,
    scope: ["openid", "email", "profile"], // Include 'profile' for name/image
  },
  github: {
    clientId: env.GITHUB_CLIENT_ID,
    clientSecret: env.GITHUB_CLIENT_SECRET,
    scope: ["user:email", "read:user"], // 'read:user' for full profile
  },
}

问题 11:Drizzle 架构的 TypeScript 错误

问题:TypeScript 对架构类型报错。

症状:出现 Type 'DrizzleD1Database' is not assignable to...

解决方案:从数据库导出正确的类型:

// src/db/index.ts
import { drizzle, type DrizzleD1Database } from "drizzle-orm/d1";
import * as schema from "./schema";

export type Database = DrizzleD1Database<typeof schema>;

export function createDatabase(d1: D1Database): Database {
  return drizzle(d1, { schema });
}

问题 12:Wrangler 开发模式无法使用

问题:wrangler dev 因数据库错误而失败。

症状:本地开发时出现“找不到数据库”或迁移错误。

解决方案:先在本地应用迁移:

# Apply migrations to local D1
wrangler d1 migrations apply my-app-db --local

# Then run dev server
wrangler dev

问题 13:用户数据更新未反映到 UI 中(使用 TanStack Query 时)

问题:更新用户数据(例如头像、姓名)后,即使调用了 queryClient.invalidateQueries(),useSession() 中的内容仍未更新。

症状:成功更新后,头像图片或用户资料数据仍显示旧内容。TanStack Query 缓存显示的是更新后的数据,但 better-auth 会话仍显示旧值。

根本原因:better-auth 使用 nanostores 管理会话状态,而非 TanStack Query。调用 queryClient.invalidateQueries() 只会使 React Query 缓存失效,不会影响 better-auth nanostore。

解决方案:更新用户数据后,手动通知 nanostore:

// Update user data
const { data, error } = await authClient.updateUser({
  image: newAvatarUrl,
  name: newName
})

if (!error) {
  // Manually invalidate better-auth session state
  authClient.$store.notify('$sessionSignal')

  // Optional: Also invalidate React Query if using it for other data
  queryClient.invalidateQueries({ queryKey: ['user-profile'] })
}

适用情况:

  • 同时使用 better-auth 和 TanStack Query
  • 更新用户资料字段(姓名、图片、电子邮件)
  • 任何会在客户端修改会话用户数据的操作

替代方案:调用 useSession() 中的 refetch(),但 $store.notify() 更直接:

const { data: session, refetch } = authClient.useSession()
// After update
await refetch()

注意:$store 是未文档化的内部 API。此模式已在生产环境中验证,但未来的 better-auth 版本可能会有所更改。

来源:社区发现的模式,已验证用于生产环境


迁移指南

从 Clerk 迁移

主要区别:

  • Clerk:第三方服务 → better-auth:可自托管
  • Clerk:专有软件 → better-auth:开源
  • Clerk:按月收费 → better-auth:免费

迁移步骤:

  1. 从 Clerk 导出用户数据(CSV 或 API)
  2. 导入 better-auth 数据库:
    // migration script
    const clerkUsers = await fetchClerkUsers();
    
    for (const clerkUser of clerkUsers) {
      await db.insert(user).values({
        id: clerkUser.id,
        email: clerkUser.email,
        emailVerified: clerkUser.email_verified,
        name: clerkUser.first_name + " " + clerkUser.last_name,
        image: clerkUser.profile_image_url,
      });
    }
    
  3. 将 Clerk SDK 替换为 better-auth 客户端:
    // Before (Clerk)
    import { useUser } from "@clerk/nextjs";
    const { user } = useUser();
    
    // After (better-auth)
    import { authClient } from "@/lib/auth-client";
    const { data: session } = authClient.useSession();
    const user = session?.user;
    
  4. 更新中间件以验证会话
  5. 配置社交登录提供方(使用相同的 OAuth 应用,但配置不同)

从 Auth.js(NextAuth)迁移

主要区别:

  • Auth.js:功能有限 → better-auth:功能全面(2FA、组织等)
  • Auth.js:依赖大量回调 → better-auth:基于插件
  • Auth.js:会话处理方式不一 → better-auth:一致

迁移步骤:

  1. 数据库架构:Auth.js 和 better-auth 使用相似的架构,但列名不同
  2. 替换配置:
    // Before (Auth.js)
    import NextAuth from "next-auth";
    import GoogleProvider from "next-auth/providers/google";
    
    export default NextAuth({
      providers: [GoogleProvider({ /* ... */ })],
    });
    
    // After (better-auth)
    import { betterAuth } from "better-auth";
    
    export const auth = betterAuth({
      socialProviders: {
        google: { /* ... */ },
      },
    });
    
  3. 更新客户端钩子:
    // Before
    import { useSession } from "next-auth/react";
    
    // After
    import { authClient } from "@/lib/auth-client";
    const { data: session } = authClient.useSession();
    

更多资源

官方文档
核心概念
身份验证方法
插件文档

核心插件:

无密码插件:

高级插件:

框架集成
社区与支持
相关文档

生产环境示例

已验证可运行的 D1 仓库(均使用 Drizzle 或 Kysely):

  1. zpg6/better-auth-cloudflare - Drizzle + D1(包含 CLI)
  2. zwily/example-react-router-cloudflare-d1-drizzle-better-auth - Drizzle + D1
  3. foxlau/react-router-v7-better-auth - Drizzle + D1
  4. matthewlynch/better-auth-react-router-cloudflare-d1 - Kysely + D1

没有任何项目使用直接的 d1Adapter——均需要 Drizzle/Kysely。


版本兼容性

测试版本:

破坏性变更:升级时请查看更新日志:https://github.com/better-auth/better-auth/releases


令牌效率:

  • 不使用 skill:约 28,000 个令牌(D1 适配器错误、TanStack Start Cookie、nanostore 失效、OAuth 流程、API 发现)
  • 使用 skill:约 5,600 个令牌(聚焦于错误、破坏性变更和 API 参考)
  • 节省:约 80%(约 22,400 个令牌)

预防的错误:13 个有明确解决方案的已记录问题 核心价值:D1 适配器要求、v1.4.0/v1.3 破坏性变更、TanStack Start 修复、nanostore 模式、80 多个端点参考


上次验证时间:2025-11-22 | Skill 版本:3.0.0 | 变更:补充了 v1.4.0(仅 ESM、无状态会话、SCIM)和 v1.3(SSO/SAML、多团队)的知识缺口。删除了教程/设置内容(约 700 行)。聚焦于错误预防、破坏性变更和 API 参考。

评论

支持 Markdown