从 Auth.js 迁移到 Better Auth
介绍
【Introduction】
在本指南中,我们将介绍将项目从 Auth.js(前身为 NextAuth.js)迁移到 Better Auth 的步骤。由于这些项目的设计理念不同,迁移需要仔细规划和执行。如果你当前的设置运行良好,则无需紧急迁移。我们将继续处理 Auth.js 的安全补丁和关键问题。
【In this guide, we'll walk through the steps to migrate a project from Auth.js (formerly NextAuth.js) to Better Auth. Since these projects have different design philosophies, the migration requires careful planning and work. If your current setup is working well, there's no urgent need to migrate. We continue to handle security patches and critical issues for Auth.js.】
然而,如果你正在启动一个新项目或在现有设置中遇到挑战,我们强烈建议使用 Better Auth。我们的路线图包括之前仅限于 Auth.js 的功能,我们希望这能在不造成分裂的情况下更紧密地团结整个生态系统。
【However, if you're starting a new project or facing challenges with your current setup, we strongly recommend using Better Auth. Our roadmap includes features previously exclusive to Auth.js, and we hope this will unite the ecosystem more strongly without causing fragmentation.】
创建更好的认证实例
【Create Better Auth Instance】
在开始迁移过程之前,请在你的项目中设置 Better Auth。请按照安装指南开始操作。
【Before starting the migration process, set up Better Auth in your project. Follow the installation guide to get started.】
例如,如果你使用 GitHub OAuth 提供者,这里是 auth.ts 文件的一个比较。
【For example, if you use the GitHub OAuth provider, here is a comparison of the auth.ts file.】
import NextAuth from "next-auth"
import GitHub from "next-auth/providers/github"
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [GitHub],
})import { betterAuth } from "better-auth";
export const auth = betterAuth({
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
})现在 Better Auth 支持无数据库的无状态会话管理。如果你在 Auth.js 中使用过数据库适配器,可以参考下面的 数据库模型 来查看与 Better Auth 核心架构的差异。
创建客户端实例
【Create Client Instance】
此客户端实例包含一组用于与 Better Auth 服务器实例交互的功能。更多信息,请参见这里。
【This client instance includes a set of functions for interacting with the Better Auth server instance. For more information, see here.】
import { createAuthClient } from "better-auth/react"
export const authClient = createAuthClient()更新路由处理程序
【Update the Route Handler】
将 /app/api/auth/[...nextauth] 文件夹重命名为 /app/api/auth/[...all] 以避免混淆。然后,按如下方式更新 route.ts 文件:
【Rename the /app/api/auth/[...nextauth] folder to /app/api/auth/[...all] to avoid confusion. Then, update the route.ts file as follows:】
import { handlers } from "@/lib/auth"
export const { GET, POST } = handlersimport { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { POST, GET } = toNextJsHandler(auth)会话管理
【Session Management】
在本节中,我们将探讨如何在 Better Auth 中管理会话,并与 Auth.js 进行比较。有关更多信息,请参见这里。
【In this section, we'll look at how to manage sessions in Better Auth compared to Auth.js. For more information, see here.】
客户端
【Client-side】
登录
【Sign In】
以下是 Auth.js 和 Better Auth 在用户登录上的区别。例如,对于 GitHub OAuth 提供商:
【Here are the differences between Auth.js and Better Auth for signing in users. For example, with the GitHub OAuth provider:】
"use client"
import { signIn } from "next-auth/react"
signIn("github")"use client"
import { authClient } from "@/lib/auth-client";
const { data, error } = await authClient.signIn.social({
provider: "github",
})登出
【Sign Out】
以下是 Auth.js 与 Better Auth 在注销用户时的区别。
【Here are the differences between Auth.js and Better Auth when signing out users.】
"use client"
import { signOut } from "next-auth/react"
signOut()"use client"
import { authClient } from "@/lib/auth-client";
const { data, error } = await authClient.signOut()获取会话
【Get Session】
以下是 Auth.js 与 Better Auth 在获取当前活动会话时的区别。
【Here are the differences between Auth.js and Better Auth for getting the current active session.】
"use client"
import { useSession } from "next-auth/react"
const { data, status, update } = useSession()"use client"
import { authClient } from "@/lib/auth-client";
const { data, error, refetch, isPending, isRefetching } = authClient.useSession()服务器端
【Server-side】
登录
【Sign In】
以下是 Auth.js 和 Better Auth 在用户登录上的区别。例如,对于 GitHub OAuth 提供商:
【Here are the differences between Auth.js and Better Auth for signing in users. For example, with the GitHub OAuth provider:】
import { signIn } from "@/lib/auth"
await signIn("github")import { auth } from "@/lib/auth";
const { redirect, url } = await auth.api.signInSocial({
body: {
provider: "github",
},
})登出
【Sign Out】
以下是 Auth.js 与 Better Auth 在注销用户时的区别。
【Here are the differences between Auth.js and Better Auth when signing out users.】
import { signOut } from "@/lib/auth"
await signOut()import { auth } from "@/lib/auth";
import { headers } from "next/headers";
const { success } = await auth.api.signOut({
headers: await headers(),
})获取会话
【Get Session】
以下是 Auth.js 与 Better Auth 在获取当前活动会话时的区别。
【Here are the differences between Auth.js and Better Auth for getting the current active session.】
import { auth } from "@/lib/auth";
const session = await auth()import { auth } from "@/lib/auth";
import { headers } from "next/headers";
const session = await auth.api.getSession({
headers: await headers(),
})保护资源
【Protecting Resources】
代理(中间件)并不适用于慢速数据获取。虽然代理对于基于权限的重定向等乐观检查很有帮助,但它不应作为完整的会话管理或授权解决方案使用。 - Next.js 文档
Auth.js 提供了使用 Proxy(中间件)的方法,但我们建议在每个页面或路由上处理身份验证检查,而不是在 Proxy 或布局中进行。下面是使用 Better Auth 保护资源的基本示例。
【Auth.js offers approaches using Proxy (Middleware), but we recommend handling auth checks on each page or route rather than in a Proxy or Layout. Here is a basic example of protecting resources with Better Auth.】
"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;import { auth } from "@/lib/auth";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
const DashboardPage = async () => {
const session = await auth.api.getSession({
headers: await headers(),
});
if (!session) {
redirect("/sign-in");
}
return (
<div>
<h1>Welcome {session.user.name}</h1>
</div>
);
};
export default DashboardPage;数据库模型
【Database models】
Auth.js 和 Better Auth 都提供无状态(JWT)和数据库会话策略。如果你在 Auth.js 中使用数据库会话策略,并计划在 Better Auth 中继续使用它,你还需要迁移你的数据库。
【Both Auth.js and Better Auth provide stateless (JWT) and database session strategies. If you were using the database session strategy in Auth.js and plan to continue using it in Better Auth, you will also need to migrate your database.】
就像 Auth.js 有数据库模型一样,Better Auth 也有一个核心架构。在本节中,我们将比较两者,并探讨它们之间的差异。
【Just like Auth.js has database models, Better Auth also has a core schema. In this section, we'll compare the two and explore the differences between them.】
用户 -> 用户
【User -> User】
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | Unique identifier for each user | |
| name | string | User's chosen display name | |
| string | User's email address for communication and login | ||
| emailVerified | Date | When the user's email was verified | |
| image | string | User's image url |
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | Unique identifier for each user | |
| name | string | - | User's chosen display name |
| string | - | User's email address for communication and login | |
| emailVerified | boolean | - | Whether the user's email is verified |
| image | string | User's image url | |
| createdAt | Date | - | Timestamp of when the user account was created |
| updatedAt | Date | - | Timestamp of the last update to the user's information |
会话 -> 会话
【Session -> Session】
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | Unique identifier for each session | |
| userId | string | The ID of the user | |
| sessionToken | string | - | The unique session token |
| expires | Date | - | The time when the session expires |
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | Unique identifier for each session | |
| userId | string | The ID of the user | |
| token | string | - | The unique session token |
| expiresAt | Date | - | The time when the session expires |
| ipAddress | string | The IP address of the device | |
| userAgent | string | The user agent information of the device | |
| createdAt | Date | - | Timestamp of when the session was created |
| updatedAt | Date | - | Timestamp of when the session was updated |
账户 -> 账户
【Account -> Account】
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | Unique identifier for each account | |
| userId | string | The ID of the user | |
| type | string | - | Type of the account (e.g. 'oauth', 'email', 'credentials') |
| provider | string | - | Provider identifier |
| providerAccountId | string | - | Account ID from the provider |
| refresh_token | string | The refresh token of the account. Returned by the provider | |
| access_token | string | The access token of the account. Returned by the provider | |
| expires_at | number | The time when the access token expires | |
| token_type | string | Type of the token | |
| scope | string | The scope of the account. Returned by the provider | |
| id_token | string | The ID token returned from the provider | |
| session_state | string | OAuth session state |
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | Unique identifier for each account | |
| userId | string | The ID of the user | |
| accountId | string | - | The ID of the account as provided by the SSO or equal to userId for credential accounts |
| providerId | string | - | The ID of the provider |
| accessToken | string | The access token of the account. Returned by the provider | |
| refreshToken | string | The refresh token of the account. Returned by the provider | |
| accessTokenExpiresAt | Date | The time when the access token expires | |
| refreshTokenExpiresAt | Date | The time when the refresh token expires | |
| scope | string | The scope of the account. Returned by the provider | |
| idToken | string | The ID token returned from the provider | |
| password | string | The password of the account. Mainly used for email and password authentication | |
| createdAt | Date | - | Timestamp of when the account was created |
| updatedAt | Date | - | Timestamp of when the account was updated |
验证令牌 -> 验证
【VerificationToken -> Verification】
| Field Name | Type | Key | Description |
|---|---|---|---|
| identifier | string | Identifier for the verification request | |
| token | string | The verification token | |
| expires | Date | - | The time when the verification token expires |
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | Unique identifier for each verification | |
| identifier | string | - | The identifier for the verification request |
| value | string | - | The value to be verified |
| expiresAt | Date | - | The time when the verification request expires |
| createdAt | Date | - | Timestamp of when the verification request was created |
| updatedAt | Date | - | Timestamp of when the verification request was updated |
比较
【Comparison】
表格: 用户
- 在 Better Auth 中,
name、email和emailVerified是必填项,而在 Auth.js 中是可选的 emailVerified在 Better Auth 中使用布尔值,而在 Auth.js 中使用时间戳- Better Auth 包含
createdAt和updatedAt时间戳
表格: 会话
- Better Auth 使用
token代替sessionToken - Better Auth 使用
expiresAt代替expires - Better Auth 包含
ipAddress和userAgent字段 - Better Auth 包含
createdAt和updatedAt时间戳
表格: 账户
- Better Auth 使用 camelCase 命名(例如
refreshToken而不是refresh_token) - Better Auth 包含
accountId来区分账户 ID 和内部 ID - Better Auth 使用
providerId而不是provider - Better Auth 包含
accessTokenExpiresAt和refreshTokenExpiresAt用于令牌管理 - Better Auth 包含
password字段以支持内置凭证认证 - Better Auth 没有
type字段,因为它由providerId决定 - Better Auth 移除了
token_type和session_state字段 - Better Auth 包含
createdAt和updatedAt时间戳
表: VerificationToken -> Verification
- Better Auth 使用
Verification表而不是VerificationToken - Better Auth 使用单一的
id主键而不是复合主键 - Better Auth 使用
value而不是token来支持各种验证类型 - Better Auth 使用
expiresAt而不是expires - Better Auth 包含
createdAt和updatedAt时间戳
如果你使用的是 Auth.js v4,请注意 v5 对数据库模式没有引入任何破坏性更改。如果你没有使用,可选字段如 oauth_token_secret 和 oauth_token 可以删除。像 refresh_token_expires_in 这样很少使用的字段也可以删除。
自定义
【Customization】
你可能已经扩展了数据库模型或在 Auth.js 中实现了额外的逻辑。Better Auth 允许你以类型安全的方式自定义核心架构。你还可以在数据库操作的生命周期中定义自定义逻辑。欲了解更多详情,请参阅 概念 - 数据库。
总结
【Wrapping Up】
现在你已经准备好从 Auth.js 迁移到 Better Auth 了。如需完整实现多种认证方法,请查看 Next.js 演示应用。Better Auth 提供更高的灵活性和更多功能,因此一定要浏览 文档 来充分发挥其潜力。
【Now you're ready to migrate from Auth.js to Better Auth. For a complete implementation with multiple authentication methods, check out the Next.js Demo App. Better Auth offers greater flexibility and more features, so be sure to explore the documentation to unlock its full potential.】
如果你在迁移过程中需要帮助,请加入我们的社区或联系 contact@better-auth.com。
【If you need help with migration, join our community or reach out to contact@better-auth.com.】