Next.js 中 better-auth 客户端 session 缺少自定义 role 属性怎么办?
先让字段配置与读取路径保持一致。如果角色属于用户,且代码通过 session.user.role 读取,就应将 role 配置在 user.additionalFields 下。同时配置客户端字段类型推断,等待 session 加载完成后再判断权限。
现有材料还不足以确定唯一原因。如果同一次读取中 session.user.role 确实有值,说明这次运行时数据包含该字段。此时还需要区分编辑器类型提示、对象查看方式和不同加载时刻的影响,不能直接认定接口没有返回 role。
1. 将 role 配置为用户字段
原配置使用的 session.additionalFields 扩展会话记录,user.additionalFields 则扩展用户记录。假设 useSession() 返回的 data 被命名为 session,两种字段的位置分别是:
- 用户字段:
session.user.role。 - 会话字段:
session.session.role。
Better Auth 返回的数据包含 user 和 session 两部分,同名字段不会自动在两者之间复制。字段与类型说明
针对题目中的用户角色用途,可以这样调整 auth.ts:
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import prisma from "@/lib/prisma";
export const auth = betterAuth({
database: prismaAdapter(prisma, {
provider: "postgresql",
}),
emailAndPassword: {
enabled: true,
},
user: {
additionalFields: {
role: {
type: "string",
defaultValue: "CUSTOMER",
input: false,
},
},
},
});
input: false 用于禁止用户通过普通用户输入设置角色。角色应由受信任的服务端逻辑管理。additionalFields 配置说明
2. 同步 Prisma schema 和数据库
修改 auth.ts 后,确认 Prisma 用户模型和实际数据库表中都存在对应的 role 字段,再完成迁移并生成 Prisma Client。Better Auth 的配置不能代替数据库迁移。
使用 Prisma adapter 时,可以通过与所用 Better Auth 版本适配的 CLI 生成 schema,再通过项目的 Prisma 迁移流程应用变更。Better Auth 内置的直接迁移功能适用于 Kysely adapter,Prisma 应通过 ORM 处理迁移。数据库迁移说明
同时检查现有用户的角色值。配置中的 defaultValue 不能说明历史记录已经回填,也不会自动将原来会话记录中的角色搬到用户记录。
3. 在客户端启用自定义字段类型推断
如果浏览器能读取 role,但 TypeScript 提示属性不存在,应检查客户端类型推断。前后端在同一项目中时,可以这样配置 auth-client.ts:
import { createAuthClient } from "better-auth/react";
import { inferAdditionalFields } from "better-auth/client/plugins";
import type { auth } from "@/lib/auth";
export const authClient = createAuthClient({
plugins: [inferAdditionalFields<typeof auth>()],
});
示例假设服务端配置位于 @/lib/auth,实际路径不同则相应调整。import type 只用于类型推断,避免将服务端认证配置作为运行时代码导入客户端。React 项目使用 better-auth/react 创建客户端。客户端字段推断说明、Next.js 客户端配置
这个插件用于类型识别,不会补充数据库中缺失的字段或角色值。不要通过 as any 掩盖配置与数据之间的问题。
4. 等待加载完成后再检查权限
原代码虽然将 isPending 放进依赖数组,却没有在判断中使用它。首次加载时,session 可能尚未返回,此时 !session 就可能触发跳转。
如果使用 Next.js App Router,可以这样组织客户端组件:
"use client";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { authClient } from "@/lib/auth-client";
export default function CustomerPage() {
const router = useRouter();
const { data: session, isPending, error } = authClient.useSession();
useEffect(() => {
if (isPending || error) return;
if (!session || session.user.role !== "CUSTOMER") {
router.replace("/");
}
}, [session, isPending, error, router]);
if (isPending) return <p>正在检查登录状态……</p>;
if (error) return <p>无法获取登录状态,请稍后重试。</p>;
if (!session || session.user.role !== "CUSTOMER") return null;
return <div>受保护内容</div>;
}
useSession() 提供 data、isPending 和 error,应分别处理加载、请求失败和权限不足的情况。useSession 用法
在 useEffect 中使用 router.replace() 执行客户端导航;redirect() 的组件用法主要对应渲染阶段。Next.js redirect 说明
5. 根据实际响应继续定位
如果调整后仍有异常,先查看浏览器 Network 面板中的会话接口响应,再与当前组件中的值比较:
- 响应的
user.role有值,只有编辑器报错:继续检查inferAdditionalFields、类型导入,以及组件是否使用了同一个authClient实例。 - 角色只出现在响应的
session.role:字段仍位于会话对象,需要核对服务端实际生效的配置和读取路径。 - 响应中没有角色或角色为空:检查用户记录、数据库迁移,以及是否有自定义响应逻辑省略了字段。
题目没有提供 Better Auth 版本、客户端配置或接口响应,因此不能据此认定存在某个版本缺陷。如果另外启用了会话缓存,数据修正后也需要检查缓存是否仍在返回旧值。
客户端角色判断用于控制页面显示和跳转。读取受保护数据的接口、Route Handler 和 Server Action 仍应在服务端验证会话与角色;隐藏页面内容无法阻止直接请求接口。Next.js 认证保护说明
备注:内容仅供参考。
