凸积分
本节内容改编自 Convex + Better Auth 指南,如需更多信息,请参考他们的文档。
先决条件
【Prerequisites】
创建一个 Convex 项目
【Create a Convex project】
要使用 Convex + Better Auth,你首先需要一个 Convex 项目。如果你还没有,请运行以下命令开始。
【To use Convex + Better Auth, you'll first need a Convex project. If you don't have one, run the following command to get started.】
npm create convex@latest查看 Convex 文档 以了解更多关于 Convex 的信息。
【Check out the Convex docs to learn more about Convex.】
运行 convex dev
【Run convex dev】
在设置过程中运行 CLI 将初始化你的 Convex 部署(如果它尚不存在),并在整个过程中保持生成的类型为最新状态。请保持其运行。
【Running the CLI during setup will initialize your Convex deployment if it doesn't already exist, and keeps generated types current through the process. Keep it running.】
npx convex dev安装 Convex + Better Auth
【Installation of Convex + Better Auth】
以下文档假设你正在使用 Next.js。
【The following documentation assumes you're using Next.js.】
如果你没有使用 Next.js,其他框架的支持可以在 Convex 安装指南 中找到。
【If you're not using Next.js, support for other frameworks is documented in the installation guide by Convex.】
有关完整示例,请查看使用 Next.js 的 Convex + Better Auth 示例 在 GitHub 上。
安装
【Installation】
安装软件包
安装组件和 Better Auth。确保使用 Convex 的 1.25.0 或更高版本。
npm install better-auth
npm install convex@latest @convex-dev/better-authRegister the component
在你的 Convex 项目中注册 Better Auth 组件。
import { defineApp } from "convex/server";
import betterAuth from "@convex-dev/better-auth/convex.config";
const app = defineApp();
app.use(betterAuth);
export default app;Add Convex auth config
添加一个 convex/auth.config.ts 文件来将 Better Auth 配置为身份验证提供程序。
import { getAuthConfigProvider } from "@convex-dev/better-auth/auth-config";
import type { AuthConfig } from "convex/server";
export default {
providers: [getAuthConfigProvider()],
} satisfies AuthConfig;Set environment variables
生成用于加密和生成哈希的密钥。如果你已经安装了 openssl,可以使用下面的命令,或者用你喜欢的方式生成自己的密钥。
npx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)将你的网站 URL 添加到你的 Convex 部署中。
npx convex env set SITE_URL http://localhost:3000将环境变量添加到由 npx convex dev 创建的 .env.local 文件中。你的框架开发服务器会自动读取它们。
# Deployment used by \`npx convex dev\`
CONVEX_DEPLOYMENT=dev:adjective-animal-123 # team: team-name, project: project-name
NEXT_PUBLIC_CONVEX_URL=https://adjective-animal-123.convex.cloud
# Same as NEXT_PUBLIC_CONVEX_URL but ends in .site
NEXT_PUBLIC_CONVEX_SITE_URL=https://adjective-animal-123.convex.site
# Your local site URL
NEXT_PUBLIC_SITE_URL=http://localhost:3000Create a Better Auth instance
Create a Better Auth instance and initialize the component.
import { createClient, type GenericCtx } from "@convex-dev/better-auth";
import { convex } from "@convex-dev/better-auth/plugins";
import { components } from "./_generated/api";
import { DataModel } from "./_generated/dataModel";
import { query } from "./_generated/server";
import { betterAuth } from "better-auth";
import authConfig from "./auth.config";
const siteUrl = process.env.SITE_URL!;
// The component client has methods needed for integrating Convex with Better Auth,
// as well as helper methods for general use.
export const authComponent = createClient<DataModel>(components.betterAuth);
export const createAuth = (ctx: GenericCtx<DataModel>) => {
return betterAuth({
baseURL: siteUrl,
database: authComponent.adapter(ctx),
// Configure simple, non-verified email/password to get started
emailAndPassword: {
enabled: true,
requireEmailVerification: false,
},
plugins: [
// The Convex plugin is required for Convex compatibility
convex({ authConfig }),
],
});
};
// Example function for getting the current user
// Feel free to edit, omit, etc.
export const getCurrentUser = query({
args: {},
handler: async (ctx) => {
return authComponent.getAuthUser(ctx);
},
});Create a Better Auth client instance
为你的客户端创建一个更好的 Auth 客户端实例,以便与 Better Auth 服务器交互。
import { createAuthClient } from "better-auth/react";
import { convexClient } from "@convex-dev/better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [convexClient()],
});Configure Next.js server utilities
为经过身份验证的 SSR、服务器功能和路由处理程序配置一组辅助函数。
import { convexBetterAuthNextJs } from "@convex-dev/better-auth/nextjs";
export const {
handler,
preloadAuthQuery,
isAuthenticated,
getToken,
fetchAuthQuery,
fetchAuthMutation,
fetchAuthAction,
} = convexBetterAuthNextJs({
convexUrl: process.env.NEXT_PUBLIC_CONVEX_URL!,
convexSiteUrl: process.env.NEXT_PUBLIC_CONVEX_SITE_URL!,
});Mount handlers
在你的 Convex 部署上注册更好的认证路由处理程序。
import { httpRouter } from "convex/server";
import { authComponent, createAuth } from "./auth";
const http = httpRouter();
authComponent.registerRoutes(http, createAuth);
export default http;设置路由处理程序,将来自框架服务器的身份验证请求代理到你的 Convex 部署。
import { handler } from "@/lib/auth-server";
export const { GET, POST } = handler;Set up Convex client provider
用 ConvexBetterAuthProvider 组件封装你的应用。
"use client";
import { type PropsWithChildren } from "react";
import { ConvexReactClient } from "convex/react";
import { authClient } from "@/lib/auth-client";
import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
export function ConvexClientProvider({
children,
initialToken,
}: PropsWithChildren<{ initialToken?: string | null }>) {
return (
<ConvexBetterAuthProvider
client={convex}
authClient={authClient}
initialToken={initialToken}
>
{children}
</ConvexBetterAuthProvider>
);
}你完成了!
【You're done!】
你现在可以开始在 Convex 上使用 Better Auth 了。
【You're now ready to start using Better Auth with Convex.】
用法
【Usage】
查看 基本用法 指南,了解更多关于通用用法的信息。以下是针对 Next.js 的使用注意事项。
【Check out the Basic Usage guide for more information on general usage. Below are usage notes specific to Next.js.】
使用服务器组件的 SSR
【SSR with server components】
凸查询可以在服务器组件中预加载,并通过 preloadAuthQuery 和 usePreloadedAuthQuery 在客户端组件中渲染。
【Convex queries can be preloaded in server components and rendered in client
components via preloadAuthQuery and usePreloadedAuthQuery.】
在服务器组件中预加载:
【Preloading in a server component:】
import { preloadAuthQuery } from "@/lib/auth-server";
import { api } from "@/convex/_generated/api";
const Page = async () => {
const [preloadedUserQuery] = await Promise.all([
preloadAuthQuery(api.auth.getCurrentUser),
// Load multiple queries in parallel if needed
]);
return (
<div>
<Header preloadedUserQuery={preloadedUserQuery} />
</div>
);
};
export default Page;Rendering preloaded data in a client component:
import { usePreloadedAuthQuery } from "@convex-dev/better-auth/nextjs/client";
import { api } from "@/convex/_generated/api";
export const Header = ({
preloadedUserQuery,
}: {
preloadedUserQuery: Preloaded<typeof api.auth.getCurrentUser>;
}) => {
const user = usePreloadedAuthQuery(preloadedUserQuery);
return (
<div>
<h1>{user?.name}</h1>
</div>
);
};
export default Header;在服务器代码中使用 Better Auth
【Using Better Auth in server code】
Better Auth 的 auth.api 方法 通常会在你的 Next.js 服务器代码中运行,但如果 Convex 是你的后端,这些方法需要在 Convex 函数中运行。然后可以通过像 useMutation 这样的钩子从客户端调用该 Convex 函数,或者在服务器函数和其他服务器代码中使用其中一个 auth-server 工具(如 fetchAuthMutation)调用。身份验证会自动通过会话 cookie 处理。
【Better Auth's
auth.api methods would
normally run in your Next.js server code, but with Convex being your backend,
these methods need to run in a Convex function. The Convex function can then be
called from the client via hooks like useMutation or in server functions and
other server code using one of the auth-server utilities like
fetchAuthMutation. Authentication is handled automatically using session
cookies.】
这是一个使用 changePassword 方法的示例。Better Auth 的 auth.api 方法在 Convex 变更中被调用,因为我们知道该函数需要写入权限。对于读取操作,可以使用查询函数。
【Here's an example using the changePassword method. The Better Auth auth.api
method is called inside of a Convex mutation, because we know this function
needs write access. For reads a query function can be used.】
import { mutation } from "./_generated/server";
import { v } from "convex/values";
import { createAuth, authComponent } from "./auth";
export const updateUserPassword = mutation({
args: {
currentPassword: v.string(),
newPassword: v.string(),
},
handler: async (ctx, args) => {
const { auth, headers } = await authComponent.getAuth(createAuth, ctx);
await auth.api.changePassword({
body: {
currentPassword: args.currentPassword,
newPassword: args.newPassword,
},
headers,
});
},
});在这里,我们从服务器操作调用变更。
【Here we call the mutation from a server action.】
"use server";
import { fetchAuthMutation } from "@/lib/auth-server";
import { api } from "../convex/_generated/api";
// Authenticated mutation via server function
export async function updatePassword({
currentPassword,
newPassword,
}: {
currentPassword: string;
newPassword: string;
}) {
await fetchAuthMutation(api.users.updatePassword, {
currentPassword,
newPassword,
});
}