钩子
了解如何使用前置和后置钩子来自定义端点行为、修改请求和响应、处理 Cookie、抛出错误、访问认证上下文以及运行后台任务。
Better Auth 中的钩子允许你“钩入”生命周期并执行自定义逻辑。它们提供了一种在不编写完整插件的情况下自定义 Better Auth 行为的方式。
如果你需要对端点进行自定义调整,而不是在 Better Auth 之外创建另一个端点,我们强烈建议使用钩子。
前置钩子(Before Hooks)
前置钩子在端点执行之前运行。可以用来修改请求、预先验证数据或提前返回。
示例:强制邮箱域名限制
此钩子确保用户只有在邮箱后缀为 @example.com 时才能注册:
import { betterAuth } from "better-auth";
import { createAuthMiddleware, APIError } from "better-auth/api";
export const auth = betterAuth({
hooks: {
before: createAuthMiddleware(async (ctx) => {
if (ctx.path !== "/sign-up/email") {
return;
}
if (!ctx.body?.email.endsWith("@example.com")) {
throw new APIError("BAD_REQUEST", {
message: "邮箱必须以 @example.com 结尾",
});
}
}),
},
});示例:修改请求上下文
在继续之前调整请求上下文:
import { betterAuth } from "better-auth";
import { createAuthMiddleware } from "better-auth/api";
export const auth = betterAuth({
hooks: {
before: createAuthMiddleware(async (ctx) => {
if (ctx.path === "/sign-up/email") {
return {
context: {
...ctx,
body: {
...ctx.body,
name: "John Doe",
},
}
};
}
}),
},
});后置钩子(After Hooks)
后置钩子在端点执行之后运行。可以用来修改响应。
示例:新用户注册时向你的频道发送通知
import { betterAuth } from "better-auth";
import { createAuthMiddleware } from "better-auth/api";
import { sendMessage } from "@/lib/notification"
export const auth = betterAuth({
hooks: {
after: createAuthMiddleware(async (ctx) => {
if(ctx.path.startsWith("/sign-up")){
const newSession = ctx.context.newSession;
if(newSession){
sendMessage({
type: "user-register",
name: newSession.user.name,
})
}
}
}),
},
});示例:在单个钩子中处理多个端点
由于 before 和 after 都只接受一次 createAuthMiddleware 调用,因此可以通过在同一个钩子内对 ctx.path 进行条件判断来处理多个端点:
import { betterAuth } from "better-auth";
import { createAuthMiddleware } from "better-auth/api";
export const auth = betterAuth({
hooks: {
after: createAuthMiddleware(async (ctx) => {
if (ctx.path === "/reset-password") {
// 密码重置后自动登录用户
}
if (ctx.path.startsWith("/sign-up")) {
// 注册后发送欢迎通知
}
if (ctx.path === "/sign-in/email") {
// 跟踪登录分析
}
}),
},
});每个钩子(before / after)都只接受一个中间件函数,而不是数组。若要为不同端点运行逻辑,请在该单个函数内部基于 ctx.path 分支处理。
Ctx
调用 createAuthMiddleware 时会传入一个 ctx 对象,提供了许多有用的属性,包括:
- Path:
ctx.path用于获取当前端点路径。 - Body:
ctx.body用于解析请求体(仅适用于 POST 请求)。 - Headers:
ctx.headers用于访问请求头。 - Request:
ctx.request用于访问请求对象(在仅服务端端点中可能不存在)。 - Query Parameters:
ctx.query用于访问查询参数。 - Context:
ctx.context认证相关上下文,用于访问新创建的会话、Cookie 配置、密码哈希器、配置等。
以及更多信息。
请求响应
此工具允许你获取请求信息并从钩子中发送响应。
JSON 响应
使用 ctx.json 发送 JSON 响应:
import { createAuthMiddleware } from "better-auth/api";
const hook = createAuthMiddleware(async (ctx) => {
return ctx.json({
message: "Hello World",
});
});重定向
使用 ctx.redirect 重定向用户:
import { createAuthMiddleware } from "better-auth/api";
const hook = createAuthMiddleware(async (ctx) => {
throw ctx.redirect("/sign-up/name");
});Cookie
- Set cookies:
ctx.setCookie或ctx.setSignedCookie。 - Get cookies:
ctx.getCookie或ctx.getSignedCookie。
示例:
import { createAuthMiddleware } from "better-auth/api";
const hook = createAuthMiddleware(async (ctx) => {
ctx.setCookie("my-cookie", "value");
await ctx.setSignedCookie("my-signed-cookie", "value", ctx.context.secret, {
maxAge: 1000,
});
const cookie = ctx.getCookie("my-cookie");
const signedCookie = await ctx.getSignedCookie("my-signed-cookie", ctx.context.secret);
});错误处理
使用 APIError 抛出特定状态码和消息的错误:
import { createAuthMiddleware, APIError } from "better-auth/api";
const hook = createAuthMiddleware(async (ctx) => {
throw new APIError("BAD_REQUEST", {
message: "无效请求",
});
});Context
ctx 对象内部包含另一个 context 对象,用于存储与认证相关的上下文信息,包括新创建的会话、Cookie 配置、密码哈希器等。
新会话
在端点执行后新创建的会话,仅存在于后置钩子中。
import { createAuthMiddleware } from "better-auth/api";
createAuthMiddleware(async (ctx) => {
const newSession = ctx.context.newSession
});返回值
钩子的返回值会传递给链中的下一个钩子。
import { createAuthMiddleware } from "better-auth/api";
createAuthMiddleware(async (ctx) => {
const returned = ctx.context.returned; // 这可能是成功响应或 APIError
});响应头
由端点和在该钩子之前运行的钩子添加的响应头。
import { createAuthMiddleware } from "better-auth/api";
createAuthMiddleware(async (ctx) => {
const responseHeaders = ctx.context.responseHeaders;
});预定义认证 Cookie
访问 BetterAuth 的预定义 Cookie 属性:
import { createAuthMiddleware } from "better-auth/api";
createAuthMiddleware(async (ctx) => {
const cookieName = ctx.context.authCookies.sessionToken.name;
});Secret
可通过 ctx.context.secret 访问你的认证实例的 secret。
密码
密码对象提供了 hash 和 verify 方法:
ctx.context.password.hash: 用于对给定密码进行哈希处理。ctx.context.password.verify: 用于验证给定的密码和哈希值。
适配器(Adapter)
适配器暴露了 Better Auth 使用的适配器方法,包括 findOne、findMany、create、delete、update 和 updateMany。通常建议使用你的 ORM 中的实际 db 实例,而不是该适配器。
Internal Adapter
如果你想使用数据库查询并获得 databaseHooks、适当的 secondaryStorage 支持等功能,使用这些内部适配器调用会很有帮助。如果你正在执行类似内部适配器操作的查询,建议考虑使用它们。
生成 ID
你可以使用 ctx.context.generateId 来生成各种用途的 ID。
runInBackground
安排任务在响应发送后执行。适用于“触发即忘”的操作(如清理、分析、速率限制计数更新等)。你可以在 advanced.backgroundTasks 中配置处理器。
import { betterAuth } from "better-auth";
import { createAuthMiddleware } from "better-auth/api";
export const auth = betterAuth({
hooks: {
after: createAuthMiddleware(async (ctx) => {
if (ctx.path.startsWith("/sign-up")) {
const newSession = ctx.context.newSession;
if (newSession) {
ctx.context.runInBackground(sendAnalyticsEvent(newSession.user.id));
}
}
}),
},
});runInBackgroundOrAwait
如果配置了处理器,则延迟执行任务,否则等待其完成。适用于必须完成但有处理器时不阻塞的操作(例如发送邮件)。处理器配置见 advanced.backgroundTasks。
import { betterAuth } from "better-auth";
import { createAuthMiddleware } from "better-auth/api";
export const auth = betterAuth({
hooks: {
after: createAuthMiddleware(async (ctx) => {
if (ctx.path.startsWith("/sign-up")) {
const newSession = ctx.context.newSession;
if (newSession) {
await ctx.context.runInBackgroundOrAwait(
sendWelcomeEmail(newSession.user)
);
}
}
}),
},
});可复用钩子
如果你需要在多个端点之间复用钩子,建议考虑创建插件。更多内容请参见 插件文档。