Better Auth 插件开发指南:从零开始构建生日验证插件
前言
在现代Web应用开发中,认证系统是保障应用安全的核心组件。Better Auth作为一个现代化的认证解决方案,提供了强大的插件机制,允许开发者扩展其核心功能。本文将带领你从零开始开发一个生日验证插件,通过这个实践过程,你将掌握Better Auth插件开发的核心概念和技巧。
准备工作
在开始之前,请确保你已经完成了Better Auth的基础配置。这个插件开发指南假设你已经:
- 完成了Better Auth的初始安装
- 配置好了基本的认证环境
- 熟悉TypeScript的基本语法
插件规划
我们计划开发一个生日验证插件,主要功能包括:
- 记录用户的生日信息
- 验证用户年龄是否超过5岁
- 提供前后端一致的验证逻辑
服务器插件开发
Better Auth的插件系统采用前后端分离的设计模式,我们先从服务器端插件开始。
基础结构创建
首先创建一个名为birthday-plugin
的文件夹,并在其中创建index.ts
文件:
birthday-plugin/
└── index.ts
在index.ts
中,我们定义插件的基本结构:
import { createAuthClient } from "better-auth/client";
import type { BetterAuthPlugin } from "better-auth";
export const birthdayPlugin = () =>
({
id: "birthdayPlugin",
} satisfies BetterAuthPlugin);
虽然这只是一个空壳,但已经是一个合法的Better Auth插件了。
数据模型定义
为了存储用户的生日信息,我们需要扩展用户模型:
export const birthdayPlugin = () =>
({
id: "birthdayPlugin",
schema: {
user: {
fields: {
birthday: {
type: "date", // 支持string, number, boolean, date类型
required: true, // 该字段是否为必填项
unique: false, // 该字段是否需要唯一
references: null // 关联其他表的引用
},
},
},
},
} satisfies BetterAuthPlugin);
这个模式定义会被Better Auth的CLI工具用来生成数据库迁移脚本。
认证逻辑实现
我们需要在用户注册时验证其年龄是否超过5岁。这里使用Better Auth的钩子(Hooks)系统:
import { APIError } from "better-auth/api";
import { createAuthMiddleware } from "better-auth/plugins";
export const birthdayPlugin = () => ({
// ...之前的配置
hooks: {
before: [
{
matcher: (context) => context.path.startsWith("/sign-up/email"),
handler: createAuthMiddleware(async (ctx) => {
const { birthday } = ctx.body;
if(!(birthday instanceof Date)) {
throw new APIError("BAD_REQUEST", {
message: "生日必须是Date类型"
});
}
const today = new Date();
const fiveYearsAgo = new Date(
today.setFullYear(today.getFullYear() - 5)
);
if(birthday >= fiveYearsAgo) {
throw new APIError("BAD_REQUEST", {
message: "用户年龄必须超过5岁"
});
}
return { context: ctx };
}),
},
],
},
} satisfies BetterAuthPlugin)
这段代码实现了:
- 验证生日字段是否为Date类型
- 计算5年前的日期
- 比较用户生日是否早于5年前
- 根据结果允许或拒绝注册请求
客户端插件开发
虽然我们的生日插件主要逻辑在服务端,但为了保持一致性,我们仍然需要创建客户端插件。
创建client.ts
文件:
birthday-plugin/
├── index.ts
└── client.ts
客户端插件代码:
import { BetterAuthClientPlugin } from "better-auth";
import type { birthdayPlugin } from "./index";
type BirthdayPlugin = typeof birthdayPlugin;
export const birthdayClientPlugin = () => {
return {
id: "birthdayPlugin",
$InferServerPlugin: {} as ReturnType<BirthdayPlugin>,
} satisfies BetterAuthClientPlugin;
};
这个客户端插件主要作用是继承服务端插件定义的类型,确保前后端类型一致。
插件集成
服务端集成
在服务端配置文件中引入插件:
import { betterAuth } from "better-auth";
import { birthdayPlugin } from "./birthday-plugin";
export const auth = betterAuth({
plugins: [
birthdayPlugin(),
]
});
客户端集成
在客户端配置文件中引入插件:
import { createAuthClient } from "better-auth/client";
import { birthdayClientPlugin } from "./birthday-plugin/client";
const authClient = createAuthClient({
plugins: [
birthdayClientPlugin()
]
});
数据库迁移
最后,执行以下命令生成数据库迁移脚本:
npx @better-auth/cli@latest generate
总结
通过本教程,我们完成了一个完整的Better Auth插件开发流程,包括:
- 插件规划与设计
- 服务端插件开发
- 数据模型定义
- 认证逻辑实现
- 客户端插件开发
- 前后端集成
这个生日验证插件虽然简单,但涵盖了Better Auth插件开发的核心概念。你可以基于这个模式开发更复杂的认证逻辑,如:
- 多因素认证
- 社交账号绑定
- 用户行为分析等
Better Auth的插件系统为认证功能的扩展提供了无限可能,希望本教程能为你开发更强大的认证功能打下坚实基础。