插件
插件是 Better Auth 的关键组成部分,它们可以扩展基础功能。你可以使用它们添加新的身份验证方法、功能,或自定义行为。
【Plugins are a key part of Better Auth, they let you extend the base functionalities. You can use them to add new authentication methods, features, or customize behaviors.】
Better Auth 提供了许多内置插件,随时可用。请查看插件部分以了解详情。你也可以创建自己的插件。
【Better Auth comes with many built-in plugins ready to use. Check the plugins section for details. You can also create your own plugins.】
使用插件
【Using a Plugin】
插件可以是服务器端插件、客户端插件,或者两者兼有。
【Plugins can be a server-side plugin, a client-side plugin, or both.】
要在服务器上添加插件,请将其包含在你的身份验证配置中的 plugins 数组中。插件将使用提供的选项进行初始化。
【To add a plugin on the server, include it in the plugins array in your auth configuration. The plugin will initialize with the provided options.】
import { betterAuth } from "better-auth";
export const auth = betterAuth({
plugins: [
// Add your plugins here
]
});客户端插件是在创建客户端时添加的。大多数插件都需要服务器端和客户端插件才能正常工作。前端的 Better Auth 认证客户端使用 better-auth/client 提供的 createAuthClient 函数。
【Client plugins are added when creating the client. Most plugins require both server and client plugins to work correctly.
The Better Auth auth client on the frontend uses the createAuthClient function provided by better-auth/client.】
import { createAuthClient } from "better-auth/client";
const authClient = createAuthClient({
plugins: [
// Add your client plugins here
]
});我们建议将 auth-client 和你常用的 auth 实例保存在不同的文件中。
创建插件
【Creating a Plugin】
要开始使用,你需要一个服务器插件。服务器插件是所有插件的核心,而客户端插件则用于提供与前端 API 的接口,以便轻松地使用你的服务器插件。
【To get started, you'll need a server plugin. Server plugins are the backbone of all plugins, and client plugins are there to provide an interface with frontend APIs to easily work with your server plugins.】
如果你的服务器插件有需要从客户端调用的端点,你也需要创建一个客户端插件。
插件能做什么?
【What can a plugin do?】
- 创建自定义
endpoint以执行任何你想要的操作。 - 使用自定义
schemas扩展数据库表。 - 使用
middleware来针对一组路由,通过其路由匹配器,并且仅在通过请求调用这些路由时运行。 - 使用
hooks来针对特定的路由或请求。如果你希望即使直接调用端点也能运行该 hook。 - 如果你想对所有请求或响应进行操作,可以使用
onRequest或onResponse。 - 创建自定义
rate-limit规则。
创建服务器插件
【Create a Server plugin】
要创建服务器插件,你需要传入一个满足 BetterAuthPlugin 接口的对象。
【To create a server plugin, you need to pass an object that satisfies the BetterAuthPlugin interface.】
唯一必需的属性是 id,它是插件的唯一标识符。服务器端和客户端插件都可以使用相同的 id。
【The only required property is id, which is a unique identifier for the plugin.
Both server and client plugins can use the same id.】
import type { BetterAuthPlugin } from "better-auth";
export const myPlugin = () => {
return {
id: "my-plugin",
} satisfies BetterAuthPlugin
}You don't have to make the plugin a function, but it's recommended to do so. This way, you can pass options to the plugin and it's consistent with the built-in plugins.
端点
【Endpoints】
要向服务器添加端点,你可以传递 endpoints,它需要一个对象,该对象的键是任意 string,值是一个 AuthEndpoint。
【To add endpoints to the server, you can pass endpoints which requires an object with the key being any string and the value being an AuthEndpoint.】
要创建一个身份验证端点,你需要从 better-auth 导入 createAuthEndpoint。
【To create an Auth Endpoint you'll need to import createAuthEndpoint from better-auth.】
Better Auth 使用名为 Better Call 的另一个库来创建端点。Better Call 是一个由 Better Auth 团队开发的简单 TypeScript 网络框架。
import { createAuthEndpoint } from "better-auth/api";
const myPlugin = () => {
return {
id: "my-plugin",
endpoints: {
getHelloWorld: createAuthEndpoint("/my-plugin/hello-world", {
method: "GET",
}, async(ctx) => {
return ctx.json({
message: "Hello World"
})
})
}
} satisfies BetterAuthPlugin
}创建 Auth 接口,它封装了 Better Call 的 createEndpoint。在 ctx 对象内部,它会提供另一个名为 context 的对象,允许你访问更好的认证特定上下文,包括 options、db、baseURL 等。
【Create Auth endpoints wraps around createEndpoint from Better Call. Inside the ctx object, it'll provide another object called context that give you access better-auth specific contexts including options, db, baseURL and more.】
上下文对象
appName:应用的名称。默认值为“Better Auth”。options:传递给 Better Auth 实例的选项。tables:核心表定义。它是一个对象,键是表名,值是模式定义。baseURL:认证服务器的 baseURL。这包括路径。例如,如果服务器运行在http://localhost:3000,默认情况下 baseURL 将是http://localhost:3000/api/auth,除非用户进行了更改。session:会话配置。包括updateAge和expiresIn的值。secret:用于各种用途的密钥。这由用户定义。authCookie:核心认证 Cookie 的默认配置。logger:Better Auth 使用的日志记录实例。db:Better Auth 用于与数据库交互的 Kysely 实例。adapter:这与 db 相同,但它提供了类似 ORM 的功能来与数据库交互。(除非你需要执行原生 SQL 查询或出于性能原因,否则我们建议使用它而不是db)internalAdapter:这些是 Better Auth 使用的内部数据库调用。例如,你可以使用这些调用来创建会话,而不是直接使用adapter。internalAdapter.createSession(userId)createAuthCookie:这是一个辅助函数,让你可以获取用于set或getcookie 的 cookiename和options。它会根据连接是否安全(HTTPS)或应用是否在生产模式下运行来实现诸如__Secure-前缀之类的功能。trustedOrigins:这是你通过options.trustedOrigins指定的受信任来源列表。isTrustedOrigin:这是一个辅助函数,允许你根据受信任来源配置快速检查给定的 URL 或路径是否受信任。
端点规则
- 确保你在端点路径中使用 kebab-case
- 请确保仅对端点使用
POST或GET方法。 - 任何修改数据的函数都应该使用
POST方法。 - 任何获取数据的函数都应该使用
GET方法。 - 请确保使用
createAuthEndpoint函数来创建 API 端点。 - 确保你的路径是唯一的,以避免与其他插件冲突。如果你使用的是常见路径,请在路径前加上插件名称作为前缀。(例如:使用
/my-plugin/hello-world而不是/hello-world。)
架构
【Schema】
你可以通过传递一个 schema 对象来为你的插件定义数据库模式。schema 对象的键应该是表名,值应该是模式定义。
【You can define a database schema for your plugin by passing a schema object. The schema object should have the table name as the key and the schema definition as the value.】
import { BetterAuthPlugin } from "better-auth/plugins";
const myPlugin = () => {
return {
id: "my-plugin",
schema: {
myTable: {
fields: {
name: {
type: "string"
}
},
modelName: "myTable" // optional if you want to use a different name than the key
}
}
} satisfies BetterAuthPlugin
}字段
默认情况下,better-auth 会为每个表格创建一个 id 字段。你可以通过将字段添加到 fields 对象中,为表格添加额外的字段。
【By default better-auth will create an id field for each table. You can add additional fields to the table by adding them to the fields object.】
键是列名,值是列定义。列定义可以具有以下属性:
【The key is the column name and the value is the column definition. The column definition can have the following properties:】
type:字段的类型。它可以是 string、number、boolean、date。
required:如果该字段在新记录中是必填的。(默认:false)
unique:字段是否应唯一。(默认值:false)
references:如果该字段是对另一个表的引用。(可选)它接受一个包含以下属性的对象:
model:要引用的表名。field:要引用的字段名。onDelete:当被引用的记录被删除时要采取的操作。(默认值:cascade)
其他模式属性
disableMigration:如果该表不应被迁移。(默认值:false)
const myPlugin = (opts: PluginOptions) => {
return {
id: "my-plugin",
schema: {
rateLimit: {
fields: {
key: {
type: "string",
},
},
disableMigration: opts.storage.provider !== "database",
},
},
} satisfies BetterAuthPlugin
}如果你在 user 或 session 表中添加额外字段,这些字段的类型将在调用 getSession 和 signUpEmail 时自动推断。
【if you add additional fields to a user or session table, the types will be inferred automatically on getSession and signUpEmail calls.】
const myPlugin = () => {
return {
id: "my-plugin",
schema: {
user: {
fields: {
age: {
type: "number",
},
},
},
},
} satisfies BetterAuthPlugin
}这将向 user 表中添加一个 age 字段,所有返回 user 的接口都会包含 age 字段,并且 TypeScript 会正确推断它。
【This will add an age field to the user table and all user returning endpoints will include the age field and it'll be inferred properly by typescript.】
不要在 user 或 session 表中存储敏感信息。如果需要存储敏感信息,请创建一个新表。
钩子
【Hooks】
钩子用于在执行操作之前或之后运行代码,无论该操作是由客户端发起还是直接在服务器上执行。你可以通过传递一个 hooks 对象来向服务器添加钩子,该对象应包含 before 和 after 属性。
【Hooks are used to run code before or after an action is performed, either from a client or directly on the server. You can add hooks to the server by passing a hooks object, which should contain before and after properties.】
import { createAuthMiddleware } from "better-auth/plugins";
const myPlugin = () => {
return {
id: "my-plugin",
hooks: {
before: [{
matcher: (context) => {
return context.headers.get("x-my-header") === "my-value"
},
handler: createAuthMiddleware(async (ctx) => {
// do something before the request
return {
context: ctx // if you want to modify the context
}
})
}],
after: [{
matcher: (context) => {
return context.path === "/sign-up/email"
},
handler: createAuthMiddleware(async (ctx) => {
return ctx.json({
message: "Hello World"
}) // if you want to modify the response
})
}]
}
} satisfies BetterAuthPlugin
}中间件
【Middleware】
你可以通过传入一个 middlewares 数组来向服务器添加中间件。这个数组应包含中间件对象,每个对象都有 path 和 middleware 属性。与钩子不同,中间件只会在客户端发起的 api 请求上运行。如果直接调用端点,中间件将不会运行。
【You can add middleware to the server by passing a middlewares array. This array should contain middleware objects, each with a path and a middleware property. Unlike hooks, middleware only runs on api requests from a client. If the endpoint is invoked directly, the middleware will not run.】
path 可以是字符串或路径匹配器,使用与 better-call 相同的路径匹配系统。
【The path can be either a string or a path matcher, using the same path-matching system as better-call.】
如果你在中间件中抛出 APIError 或返回一个 Response 对象,请求将会被停止,并且响应会发送到客户端。
【If you throw an APIError from the middleware or return a Response object, the request will be stopped, and the response will be sent to the client.】
const myPlugin = () => {
return {
id: "my-plugin",
middlewares: [
{
path: "/my-plugin/hello-world",
middleware: createAuthMiddleware(async(ctx) => {
// do something
})
}
]
} satisfies BetterAuthPlugin
}关于请求与响应
【On Request & On Response】
除了中间件之外,你还可以在请求发出之前和响应返回之后进行钩子。如果你想执行影响所有请求或响应的操作,这通常非常有用。
【Additional to middlewares, you can also hook into right before a request is made and right after a response is returned. This is mostly useful if you want to do something that affects all requests or responses.】
根据请求
【On Request】
onRequest 函数在请求发出之前调用。它接受两个参数:request 和 context 对象。
【The onRequest function is called right before the request is made. It takes two parameters: the request and the context object.】
操作方法如下:
【Here’s how it works:】
- 照常继续:如果你没有返回任何内容,请求将照常进行。
- 中断请求:要停止请求并发送响应,请返回一个包含
Response对象的response属性的对象。 - 修改请求:你也可以返回一个修改后的
request对象,以在发送请求之前更改请求内容。
const myPlugin = () => {
return {
id: "my-plugin",
onRequest: async (request, context) => {
// do something
},
} satisfies BetterAuthPlugin
}关于响应
【On Response】
onResponse 函数在响应返回后立即执行。它接受两个参数:response 和 context 对象。
【The onResponse function is executed immediately after a response is returned. It takes two parameters: the response and the context object.】
使用方法如下:
【Here’s how to use it:】
- 修改响应:你可以返回一个修改后的响应对象,以在发送给客户端之前更改响应内容。
- 正常继续:如果你不返回任何内容,响应将照常发送。
const myPlugin = () => {
return {
id: "my-plugin",
onResponse: async (response, context) => {
// do something
},
} satisfies BetterAuthPlugin
}速率限制
【Rate Limit】
你可以通过传入一个 rateLimit 数组为你的插件定义自定义速率限制规则。速率限制数组应包含一个或多个速率限制对象。
【You can define custom rate limit rules for your plugin by passing a rateLimit array. The rate limit array should contain an array of rate limit objects.】
const myPlugin = () => {
return {
id: "my-plugin",
rateLimit: [
{
pathMatcher: (path) => {
return path === "/my-plugin/hello-world"
},
limit: 10,
window: 60,
}
]
} satisfies BetterAuthPlugin
}受信任的来源
【Trusted origins】
如果你正在构建自定义插件或端点,可以使用身份验证上下文中提供的 isTrustedOrigin() 方法,根据你的可信来源配置验证 URL。 这可确保你的自定义端点遵循与 Better Auth 内置端点相同的安全设置。
【If you're building custom plugins or endpoints, you can use the isTrustedOrigin() method available on the auth context to validate URLs against your trusted origins configuration.
This ensures your custom endpoints respect the same security settings as Better Auth's built-in endpoints.】
import { createAuthEndpoint, APIError } from "better-auth/api";
import * as z from "zod"
const myPlugin = () => {
return {
id: "my-plugin",
trustedOrigins: [
"http://trusted.com"
],
endpoints: {
getTrustedHelloWorld: createAuthEndpoint("/my-plugin/hello-world", {
method: "GET",
query: z.object({
url: z.string()
}),
}, async (ctx) => {
// The allowRelativePaths option can be used to either allow or disallow relative paths
if (!ctx.context.isTrustedOrigin(ctx.query.url, { allowRelativePaths: false })) {
throw new APIError("FORBIDDEN", {
message: "origin is not trusted."
});
}
return ctx.json({
message: "Hello World"
})
})
}
} satisfies BetterAuthPlugin
}请参阅受信任的来源和安全文档以获取更多信息。
【See the trusted origins and security docs for more info.】
服务器插件辅助函数
【Server-plugin helper functions】
用于创建服务器插件的一些额外辅助函数。
【Some additional helper functions for creating server plugins.】
getSessionFromCtx
允许你通过传递身份验证中间件的 context 来获取客户端的会话数据。
【Allows you to get the client's session data by passing the auth middleware's context.】
import { createAuthMiddleware } from "better-auth/plugins";
import { getSessionFromCtx } from "better-auth/api";
const myPlugin = {
id: "my-plugin",
hooks: {
before: [{
matcher: (context) => {
return context.headers.get("x-my-header") === "my-value"
},
handler: createAuthMiddleware(async (ctx) => {
const session = await getSessionFromCtx(ctx);
// do something with the client's session.
return {
context: ctx
}
})
}],
}
} satisfies BetterAuthPluginsessionMiddleware
一个中间件,用于检查客户端是否有有效的会话。如果客户端有有效会话,它会将会话数据添加到上下文对象中。
【A middleware that checks if the client has a valid session. If the client has a valid session, it'll add the session data to the context object.】
import { createAuthMiddleware } from "better-auth/plugins";
import { sessionMiddleware } from "better-auth/api";
const myPlugin = () => {
return {
id: "my-plugin",
endpoints: {
getHelloWorld: createAuthEndpoint("/my-plugin/hello-world", {
method: "GET",
use: [sessionMiddleware],
}, async (ctx) => {
const session = ctx.context.session;
return ctx.json({
message: "Hello World"
})
})
}
} satisfies BetterAuthPlugin
}创建客户端插件
【Creating a client plugin】
如果你的端点需要从客户端调用,你还需要创建一个客户端插件。Better Auth 客户端可以从服务器插件推断端点。你也可以添加额外的客户端逻辑。
【If your endpoints need to be called from the client, you'll also need to create a client plugin. Better Auth clients can infer the endpoints from the server plugins. You can also add additional client-side logic.】
import type { BetterAuthClientPlugin } from "better-auth";
export const myPluginClient = () => {
return {
id: "my-plugin",
} satisfies BetterAuthClientPlugin
}端点接口
【Endpoint Interface】
通过在客户端插件中添加 $InferServerPlugin 键,可以从服务器插件推断出端点。
【Endpoints are inferred from the server plugin by adding a $InferServerPlugin key to the client plugin.】
客户端将 path 推断为对象,并将连字符命名转换为驼峰命名。例如,/my-plugin/hello-world 会变成 myPlugin.helloWorld。
【The client infers the path as an object and converts kebab-case to camelCase. For example, /my-plugin/hello-world becomes myPlugin.helloWorld.】
import type { BetterAuthClientPlugin } from "better-auth/client";
import type { myPlugin } from "./plugin";
const myPluginClient = () => {
return {
id: "my-plugin",
$InferServerPlugin: {} as ReturnType<typeof myPlugin>,
} satisfies BetterAuthClientPlugin
}获取操作
【Get actions】
如果你需要向客户端添加额外的方法或其他内容,可以使用 getActions 函数。这个函数会使用客户端的 fetch 函数来调用。
【If you need to add additional methods or whatnot to the client, you can use the getActions function. This function is called with the fetch function from the client.】
Better Auth 使用 Better fetch 来进行请求。Better Fetch 是由 Better Auth 的同一作者制作的一个简单的 fetch 封装。
import type { BetterAuthClientPlugin } from "better-auth/client";
import type { myPlugin } from "./plugin";
import type { BetterFetchOption } from "@better-fetch/fetch";
const myPluginClient = {
id: "my-plugin",
$InferServerPlugin: {} as ReturnType<typeof myPlugin>,
getActions: ($fetch) => {
return {
myCustomAction: async (data: {
foo: string,
}, fetchOptions?: BetterFetchOption) => {
const res = $fetch("/custom/action", {
method: "POST",
body: {
foo: data.foo
},
...fetchOptions
})
return res
}
}
}
} satisfies BetterAuthClientPlugin作为一般指导原则,确保每个函数只接受一个参数,可选择性地添加第二个 fetchOptions 参数,以便用户向 fetch 调用传递额外选项。函数应返回一个包含 data 和 error 键的对象。
如果你的使用场景涉及 API 调用以外的操作,请随意偏离此规则。
获取原子
【Get Atoms】
这只有在你想提供像 useSession 这样的 hooks 时才有用。
【This is only useful if you want to provide hooks like useSession.】
获取原子是通过 better fetch 的 fetch 函数调用的,它应该返回一个包含原子的对象。原子应该使用 nanostores 创建。原子将通过 nanostores 提供的每个框架的 useStore 钩子来解析。
import { atom } from "nanostores";
import type { BetterAuthClientPlugin } from "better-auth/client";
const myPluginClient = {
id: "my-plugin",
$InferServerPlugin: {} as ReturnType<typeof myPlugin>,
getAtoms: ($fetch) => {
const myAtom = atom<null>()
return {
myAtom
}
}
} satisfies BetterAuthClientPlugin查看内置插件,了解如何正确使用 atoms 的示例。
【See built-in plugins for examples of how to use atoms properly.】
路径方法
【Path methods】
默认情况下,如果推断出的路径不需要请求体,则使用 GET 方法;如果需要请求体,则使用 POST 方法。你可以通过传递一个 pathMethods 对象来覆盖此设置。键应为路径,值应为方法("POST" | "GET")。
【By default, inferred paths use the GET method if they don't require a body and POST if they do. You can override this by passing a pathMethods object. The key should be the path, and the value should be the method ("POST" | "GET").】
import type { BetterAuthClientPlugin } from "better-auth/client";
import type { myPlugin } from "./plugin";
const myPluginClient = {
id: "my-plugin",
$InferServerPlugin: {} as ReturnType<typeof myPlugin>,
pathMethods: {
"/my-plugin/hello-world": "POST"
}
} satisfies BetterAuthClientPlugin获取插件
【Fetch plugins】
如果你需要使用更好的 fetch 插件,可以将它们传递给 fetchPlugins 数组。你可以在 更好的 fetch 文档中阅读更多关于 fetch 插件的信息。
原子监听器
【Atom Listeners】
这只有在你想提供像 useSession 这样的 hooks,并且想监听原子状态并在它们变化时重新计算时才有用。
【This is only useful if you want to provide hooks like useSession and you want to listen to atoms and re-evaluate them when they change.】
你可以看到它是如何在内置插件中使用的。
【You can see how this is used in the built-in plugins.】