Nuxt 集成

将 Better Auth 集成到 Nuxt。

在开始之前,请确保你已经配置了 Better Auth 实例。如果还没有,查看 安装指南。

挂载处理器

将 Better Auth 处理器挂载到一个 catch-all Nitro 路由。创建文件 server/api/auth/[...all].ts:

server/api/auth/[...all].ts
import { auth } from "~~/lib/auth"; // 导入你的 auth 配置

export default defineEventHandler((event) => {
	return auth.handler(toWebRequest(event));
});

你可以在 Better Auth 配置中更改挂载路径,但建议保持为 /api/auth/[...all]。

创建客户端

使用 better-auth/vue 创建 auth 客户端,这样 useSession 会返回 Vue refs:

lib/auth-client.ts
import { createAuthClient } from "better-auth/vue";

export const authClient = createAuthClient();
export const { signIn, signUp, signOut, useSession } = authClient;

使用会话

在页面的 setup() 中使用 authClient.useSession(useFetch),即可在服务端加载会话并在客户端补水。传入 Nuxt 的 useFetch,这样请求会携带传入的 cookies,并且 payload 会在补水期间复用:

app/pages/index.vue
<script setup lang="ts">
import { authClient } from "~~/lib/auth-client";

const { data: session } = await authClient.useSession(useFetch);
</script>

<template>
	<div v-if="session">
		<p>欢迎,{{ session.user.name }}</p>
		<button @click="authClient.signOut()">退出登录</button>
	</div>
	<button v-else @click="authClient.signIn.social({ provider: 'github' })">
		使用 GitHub 继续
	</button>
</template>

对于像弹出层或菜单这类仅在客户端使用的组件,直接调用 authClient.useSession(),不要传参数。它会返回一个响应式 ref,并在登录和退出时自动更新。

保护页面

创建一个具名路由中间件,并通过 definePageMeta 将页面关联到该中间件:

app/middleware/auth.ts
import { authClient } from "~~/lib/auth-client";

export default defineNuxtRouteMiddleware(async (to) => {
	const { data: session } = await authClient.useSession(useFetch);
	if (!session.value) {
		return navigateTo({ path: "/login", query: { redirect: to.fullPath } });
	}
});
app/pages/dashboard.vue
<script setup lang="ts">
definePageMeta({ middleware: "auth" });
</script>

将文件重命名为 app/middleware/auth.global.ts,即可使其在每个路由上运行。务必对 navigateTo(...) 调用使用 return,否则它不会生效。

保护服务端路由

使用传入的 event.headers 调用 auth.api.getSession,并用 createError 对路由进行保护:

server/api/me.get.ts
import { auth } from "~~/lib/auth";

export default defineEventHandler(async (event) => {
	const session = await auth.api.getSession({ headers: event.headers });
	if (!session?.user) {
		throw createError({ statusCode: 401, statusMessage: "未授权" });
	}
	return { user: session.user };
});

如果你需要在很多路由中复用这段逻辑,可以把检查提取到你自己的 server/utils/ 辅助函数中。

在 SSR 期间使用客户端

除了 useSession(useFetch) 之外,authClient 的操作默认不会在 SSR 期间转发 cookies,因此会返回未认证状态。处理方式有两种:

方案 A:仅在客户端调用。 使用 <ClientOnly> 包裹消费者组件,或者通过 import.meta.client 做条件判断:

<script setup lang="ts">
import { authClient } from "~~/lib/auth-client";

const accounts = ref<Awaited<ReturnType<typeof authClient.listAccounts>>["data"]>();

onMounted(async () => {
	const { data } = await authClient.listAccounts();
	accounts.value = data;
});
</script>

方案 B:创建一个按请求作用域的客户端,在 SSR 期间转发 cookies。 添加一个 useAuth 可组合函数,并与 useAsyncData 结合使用:

app/composables/useAuth.ts
import { createAuthClient } from "better-auth/vue";

export function useAuth() {
	const url = useRequestURL();
	const headers = import.meta.server ? useRequestHeaders(["cookie"]) : undefined;
	return createAuthClient({
		baseURL: url.origin,
		fetchOptions: { headers },
	});
}
app/pages/settings.vue
<script setup lang="ts">
const { data: accounts } = await useAsyncData("accounts", () =>
	useAuth().listAccounts().then((res) => res.data),
);
</script>

与 Nuxt 层复用

当你想在多个 Nuxt 应用之间复用同一套 auth 配置时(例如一个营销站和一个仪表盘),可以将 lib/auth.ts、server/、app/middleware/ 和 app/composables/ 提取到一个 Nuxt 层 中,并在每个应用里继承它:

nuxt.config.ts
export default defineNuxtConfig({
	extends: ["./layers/auth"],
});

资源与示例