钩子(Hooks)

Better Auth 钩子让你可以定制 BetterAuth 的行为

Better Auth 中的钩子允许你“钩入”生命周期并执行自定义逻辑。它们提供了一种无需编写完整插件即可定制 Better Auth 行为的方式。

我们强烈建议如果你需要对某个端点进行自定义调整时,使用钩子,而不是在 Better Auth 之外创建另一个端点。

前置钩子(Before Hooks)

前置钩子在端点执行之前运行。可以用来修改请求、预先验证数据或提前返回。

示例:强制邮箱域名限制

此钩子确保用户只有当邮箱后缀为 @example.com 时才能注册:

auth.ts
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 结尾",
                });
            }
        }),
    },
});

示例:修改请求上下文

在继续前调整请求上下文:

auth.ts
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)

后置钩子在端点执行之后运行。可以用来修改响应。

示例:新用户注册时向你的频道发送通知

auth.ts
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,
                    })
                }
            }
        }),
    },
});

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:ctx.setCookiesctx.setSignedCookie
  • 获取 Cookie:ctx.getCookiesctx.getSignedCookie

示例:

import { createAuthMiddleware } from "better-auth/api";

const hook = createAuthMiddleware(async (ctx) => {
    ctx.setCookies("my-cookie", "value");
    await ctx.setSignedCookie("my-signed-cookie", "value", ctx.context.secret, {
        maxAge: 1000,
    });

    const cookie = ctx.getCookies("my-cookie");
    const signedCookie = await ctx.getSignedCookie("my-signed-cookie");
});

错误处理

使用 APIError 抛出特定状态码和消息的错误:

import { createAuthMiddleware, APIError } from "better-auth/api";

const hook = createAuthMiddleware(async (ctx) => {
    throw new APIError("BAD_REQUEST", {
        message: "无效请求",
    });
});

上下文(Context)

ctx 对象中包含另一个专门用于认证的 context 对象。它包括后置钩子中新创建的会话、Cookie 配置、密码哈希工具等内容。

新会话

端点执行后新生成的会话。仅存在于后置钩子中。

auth.ts
import { createAuthMiddleware } from "better-auth/api";

createAuthMiddleware(async (ctx) => {
    const newSession = ctx.context.newSession
});

返回值

钩子的返回值会传递给链中的下一个钩子。

auth.ts
import { createAuthMiddleware } from "better-auth/api";

createAuthMiddleware(async (ctx) => {
    const returned = ctx.context.returned; // 这可能是成功响应或 APIError
});

响应头

由端点和在该钩子之前运行的钩子添加的响应头。

auth.ts
import { createAuthMiddleware } from "better-auth/api";

createAuthMiddleware(async (ctx) => {
    const responseHeaders = ctx.context.responseHeaders;
});

访问 BetterAuth 的预定义 Cookie 属性:

auth.ts
import { createAuthMiddleware } from "better-auth/api";

createAuthMiddleware(async (ctx) => {
    const cookieName = ctx.context.authCookies.sessionToken.name;
});

Secret

可通过 ctx.context.secret 访问你的认证实例的 secret

密码

密码对象提供了 hashverify 方法:

  • ctx.context.password.hash: 用于对给定密码进行哈希。
  • ctx.context.password.verify: 用于验证给定的 passwordhash 是否匹配。

适配器(Adapter)

适配器暴露了 Better Auth 使用的适配器方法,包括 findOnefindManycreatedeleteupdateupdateMany。通常推荐使用你的 ORM 中的实际 db 实例,而非该适配器。

内部适配器

这些是对数据库执行特定操作的调用,如 createUsercreateSessionupdateSession 等。

如果你想使用数据库查询,并获得 databaseHooks、恰当的 secondaryStorage 支持等功能,使用这些内部适配器调用会很有帮助。如果你正在执行类似内部适配器操作的查询,建议考虑使用它们。

生成 ID

你可以使用 ctx.context.generateId 来生成各种用途的 ID。

runInBackground

安排任务在响应发送后执行。适用于“触发即忘”的操作(如清理、分析、速率限制计数更新等)。你可以在 advanced.backgroundTasks 中配置处理器。

auth.ts
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

auth.ts
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)
          );
        }
      }
    }),
  },
});

可复用钩子

如果你需要在多个端点之间复用钩子,建议考虑创建插件。更多内容请参见 插件文档