You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next-Auth JWT令牌未携带Role,中间件RBAC失效问题排查

Next.js + Drizzle ORM + Supabase + Auth.js RBAC 角色字段无法在JWT/中间件中获取

问题

在Next.js应用中使用Drizzle ORM、Supabase(Postgres)和Auth.js实现基于角色的访问控制(RBAC),采用Google作为OAuth提供商。目前中间件中request.auth.user.role始终为undefined,JWT令牌里也缺失role字段,尽管已在Auth.js的JWT和session回调中尝试赋值。

配置情况

  • 扩展了Auth.js的User和Session接口,新增role字段
  • Auth.ts中配置了DrizzleAdapter、JWT和session回调,尝试将用户的role传递到令牌和会话中
  • Middleware.ts使用auth()中间件根据角色实现跳转逻辑

代码片段

JWT回调

callbacks: {
  async jwt({ token, user }) {
    if (user) {
      token.role = user.role; // 此处赋值逻辑存在,但最终token无role
      token.id = user.id;
    }
    return token;
  },
  async session({ session, token }) {
    session.user.role = token.role; // 尝试传递role到会话
    return session;
  }
}

中间件逻辑

const role = request.auth?.user?.role; // 🚨 始终为undefined

已检查内容

  • 数据库user表存在role列(已通过Supabase验证)
  • DrizzleAdapter已正确关联Supabase schema
  • 会话策略设置为"jwt"

解决方案

1. 确认TypeScript接口扩展的正确性

确保在@auth/core/types的类型扩展中正确添加role字段,让Auth.js能识别自定义字段:

import { DefaultSession, DefaultUser } from "@auth/core/types";

declare module "@auth/core/types" {
  interface User extends DefaultUser {
    role: string;
  }

  interface Session extends DefaultSession {
    user: User;
  }
}

将这段代码放在Auth.ts文件顶部或单独的types/auth.d.ts文件中,确保TypeScript编译时能加载该类型扩展。

2. 验证JWT回调中user对象是否包含role

用户首次登录时,Auth.js的user参数来自DrizzleAdapter返回的数据。需要确认:

  • OAuth登录后创建用户时,数据库中user表的role字段已被正确赋值(比如设置默认值'user')
  • Drizzle的user表schema中role字段不是可选类型或有默认值
  • 在JWT回调中打印user对象,确认是否包含role:
    async jwt({ token, user }) {
      console.log('JWT回调中的user对象:', user); // 检查输出是否有role字段
      if (user) {
        token.role = user.role;
        token.id = user.id;
      }
      return token;
    },
    

3. 强制从数据库同步最新role到会话

如果用户role在数据库更新后JWT令牌未自动刷新,可在session回调中主动从数据库查询最新用户信息:

import { db } from '@/lib/db'; // 导入你的Drizzle数据库实例
import { users, eq } from '@/lib/db/schema'; // 导入user表schema

async session({ session, token }) {
  if (token.id) {
    const dbUser = await db.query.users.findFirst({
      where: eq(users.id, token.id as string),
    });
    if (dbUser) {
      session.user.role = dbUser.role;
    }
  }
  return session;
}

4. 清除旧会话缓存

旧的JWT令牌可能未包含role字段,需要让用户退出登录后重新登录,或手动清除浏览器的Auth.js相关Cookie,确保生成新的包含role的令牌。

5. 确认中间件配置正确性

确保Middleware.ts中auth()中间件的配置覆盖了需要验证的路由,且会话策略确实为"jwt":

import { auth } from "@/auth";
import { NextResponse } from "next/server";

export default auth((req) => {
  if (!req.auth) {
    return NextResponse.redirect(new URL('/login', req.url));
  }
  const role = req.auth.user.role;
  // 此处添加角色权限判断逻辑
});

export const config = {
  matcher: ['/dashboard/:path*', '/admin/:path*'], // 按需调整匹配路径
};

内容的提问来源于stack exchange,提问作者Rana Adeel Tahir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 14:05:18