从 Auth.js 迁移到 Better Auth

从 Auth.js 迁移到 Better Auth 的分步指南。

介绍

在本指南中,我们将逐步演示如何将项目从 Auth.js(前身为 NextAuth.js)迁移到 Better Auth。由于这两个项目的设计理念不同,迁移需要仔细规划和实施。如果您当前的设置运行良好,则无需紧急迁移。我们仍然会为 Auth.js 处理安全补丁和关键问题。

但如果您正在启动新项目或在当前设置中遇到挑战,我们强烈建议使用 Better Auth。我们的路线图包括以前仅 Auth.js 才有的功能,我们希望这能加强生态系统的统一,而不会导致分裂。

创建 Better Auth 实例

在开始迁移过程之前,在您的项目中设置 Better Auth。请按照安装指南开始。

例如,如果您使用 GitHub OAuth 提供程序,以下是 auth.ts 文件的对比。

import NextAuth from "next-auth"
import GitHub from "next-auth/providers/github"
  
export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [GitHub],
})

现在 Better Auth 支持无需任何数据库的无状态会话管理。如果您之前在 Auth.js 中使用了数据库适配器,可以参考下面的数据库模型以了解与 Better Auth 核心架构的差异。

创建客户端实例

此客户端实例包含一组用于与 Better Auth 服务端实例交互的函数。有关更多信息,请参阅客户端文档

auth-client.ts
import { createAuthClient } from "better-auth/react"

export const authClient = createAuthClient()

更新路由处理程序

/app/api/auth/[...nextauth] 文件夹重命名为 /app/api/auth/[...all],以避免混淆。然后,按如下方式更新 route.ts 文件:

import { handlers } from "@/lib/auth"

export const { GET, POST } = handlers

会话管理

在本节中,我们将了解 Better Auth 与 Auth.js 相比如何管理会话。有关更多信息,请参阅会话管理文档

客户端

登录

以下是在登录用户时,Auth.js 与 Better Auth 的区别。例如,使用 GitHub OAuth 提供程序:

"use client"

import { signIn } from "next-auth/react"

signIn("github")

登出

以下是在登出用户时,Auth.js 与 Better Auth 的区别。

"use client"

import { signOut } from "next-auth/react"

signOut()

获取会话

以下是在获取当前活跃会话时,Auth.js 与 Better Auth 的区别。

"use client"

import { useSession } from "next-auth/react"

const { data, status, update } = useSession()

服务端

登录

以下是在登录用户时,Auth.js 与 Better Auth 的区别。例如,使用 GitHub OAuth 提供程序:

import { signIn } from "@/lib/auth"

await signIn("github")

登出

以下是在登出用户时,Auth.js 与 Better Auth 的区别。

import { signOut } from "@/lib/auth"

await signOut()

获取会话

以下是在获取当前活跃会话时,Auth.js 与 Better Auth 的区别。

import { auth } from "@/lib/auth";

const session = await auth()

保护资源

代理(中间件)不适用于缓慢的数据获取。虽然代理对于基于权限的重定向等乐观检查可能很有用,但我们建议在每个页面或路由上处理身份验证检查,而不是在代理或布局中。以下是使用 Better Auth 保护资源的基本示例。

Auth.js 提供了使用代理(中间件)的方法,但我们建议在页面或路由本身上处理身份验证检查,而不是在代理或布局中。以下是使用 Better Auth 保护资源的基本示例。

app/dashboard/page.tsx
"use client";

import { authClient } from "@/lib/auth-client";
import { redirect } from "next/navigation";

const DashboardPage = () => {
  const { data, error, isPending } = authClient.useSession();

  if (isPending) {
    return <div>Pending</div>;
  }
  if (!data || error) {
    redirect("/sign-in");
  }

  return (
    <div>
      <h1>Welcome {data.user.name}</h1>
    </div>
  );
};

export default DashboardPage;

数据库模型

Auth.js 和 Better Auth 都提供无状态(JWT)和数据库会话策略。如果您之前在 Auth.js 中使用了数据库会话策略并计划继续在 Better Auth 中使用它,您还需要迁移数据库。

就像 Auth.js 有数据库模型一样,Better Auth 也有核心架构。在本节中,我们将对两者进行比较并探索它们之间的差异。

用户 -> 用户

Table
字段
类型
描述
id
string
PK
每个用户的唯一标识符
name ?
string
-
用户选择的显示名称
email ?
string
-
用于通信和登录的用户电子邮件地址
emailVerified ?
Date
-
用户电子邮件验证的时间
image ?
string
-
用户头像 URL

会话 -> 会话

Table
字段
类型
描述
id
string
PK
每个会话的唯一标识符
userId
string
FK
用户的 ID
sessionToken
string
-
唯一的会话令牌
expires
Date
-
会话过期时间

账户 -> 账户

Table
字段
类型
描述
id
string
PK
每个账户的唯一标识符
userId
string
FK
用户的 ID
type
string
-
账户类型(例如 'oauth'、'email'、'credentials')
provider
string
-
提供程序标识符
providerAccountId
string
-
来自提供程序的账户 ID
refresh_token ?
string
-
账户的刷新令牌,由提供程序返回
access_token ?
string
-
账户的访问令牌,由提供程序返回
expires_at ?
number
-
访问令牌过期时间
token_type ?
string
-
令牌类型
scope ?
string
-
账户的作用域,由提供程序返回
id_token ?
string
-
来自提供程序的 ID 令牌
session_state ?
string
-
OAuth 会话状态

验证令牌 -> 验证

Table
字段
类型
描述
identifier
string
PK
验证请求的标识符
token
string
PK
验证令牌
expires
Date
-
验证令牌过期时间

对比

表格: 用户

  • nameemailemailVerified 在 Better Auth 中是必填项,而在 Auth.js 中是可选的
  • Better Auth 中 emailVerified 使用布尔值,而 Auth.js 中使用时间戳
  • Better Auth 包含 createdAtupdatedAt 时间戳

表格: 会话

  • Better Auth 使用 token 而不是 sessionToken
  • Better Auth 使用 expiresAt 而不是 expires
  • Better Auth 包含 ipAddressuserAgent 字段
  • Better Auth 包含 createdAtupdatedAt 时间戳

表格: 账户

  • Better Auth 使用 camelCase 命名(例如,refreshTokenrefresh_token
  • Better Auth 使用 id 作为本地账户记录,并使用唯一的 issueraccountId 组合作为外部身份标识
  • Better Auth 使用 providerId 而不是 provider
  • Better Auth 包含用于令牌管理的 accessTokenExpiresAtrefreshTokenExpiresAt
  • Better Auth 包含 password 字段,以支持内置凭据身份验证
  • Better Auth 没有 type 字段,因为它由 providerId 决定
  • Better Auth 移除了 token_typesession_state 字段
  • Better Auth 包含 createdAtupdatedAt 时间戳

凭据迁移的关键注意事项: 在 Auth.js 中,密码通常直接存储在 User 模型中。在 Better Auth 中,密码必须存储在 Account 表中。 如果您要迁移带有密码的用户(包括 Phone Number 插件的用户),则必须为每个用户在 account 表中创建一条记录,其中 providerId: "credential"issuer: "local:credential",并将 accountId 设置为 Better Auth 用户的 id。如果没有此记录,基于密码的登录将会失败。

表格: 验证令牌 -> 验证

  • Better Auth 使用 Verification 表代替 VerificationToken
  • Better Auth 使用单一主键 id 而不是复合主键
  • Better Auth 使用 value 而不是 token 以支持各种验证类型
  • Better Auth 使用 expiresAt 而不是 expires
  • Better Auth 包含 createdAtupdatedAt 时间戳

如果您使用的是 Auth.js v4,请注意 v5 并没有对数据库架构进行任何破坏性更改。可选字段(如 oauth_token_secretoauth_token)如果未使用可以删除。不常用的字段(如 refresh_token_expires_in)也可以移除。

自定义

您可能已经扩展了 Auth.js 的数据库模型或实现了其他逻辑。Better Auth 允许您以类型安全的方式自定义核心架构。您还可以定义数据库操作的生命周期期间的自定义逻辑。更多详细信息,请参见概念 - 数据库

总结

您现在已经准备好从 Auth.js 迁移到 Better Auth。有关包含多种身份验证方式的完整实现,请参阅 Next.js 示例应用。Better Auth 提供了更大的灵活性和更多功能,请务必浏览文档,充分发挥其潜力。

如果您在迁移过程中需要帮助,请加入我们的社区,或通过 [email protected] 联系我们。