Next.js集成NextAuth+Firebase实现角色认证遇错求助
CredentialsProvider 错误配置
profile方法:
CredentialsProvider 是基于凭证的登录方案,不属于 OAuth/OpenID 类提供商,它的配置项里不存在profile字段,你复制的是 OAuth 提供商的代码片段,这会直接导致配置不合法报错。Firebase User 对象无
role属性:signInWithEmailAndPassword返回的userCredential.user是 Firebase 原生 User 对象,仅包含邮箱、UID、验证状态等基础信息,没有预设的role字段,后续回调里读取user.role会得到undefined,无法正确设置角色。authorize 方法错误处理缺陷:
代码里重复写了两次.catch块,且 catch 仅打印错误但未返回null,会导致 Promise 处于 resolved 状态但返回值为undefined,不符合 NextAuth 要求(authorize必须返回用户对象或null)。TypeScript 类型未扩展:
NextAuth 默认的 Session 和 JWT 类型不包含role属性,直接赋值会触发 TypeScript 类型校验错误。
步骤1:移除无效的 profile 配置
直接删除 CredentialsProvider 中的 profile 方法,该配置对凭证登录完全无效。
步骤2:从 Firebase 获取用户角色
提前将用户角色存储在 Firestore/Realtime Database 中,登录成功后根据用户 UID 查询角色,修改 authorize 方法:
// 导入Firestore依赖(用Firestore存角色为例) import { getFirestore, doc, getDoc } from "firebase/firestore"; const db = getFirestore(); async authorize(credentials): Promise<any> { try { if (!credentials?.email || !credentials?.password) return null; const userCredential = await signInWithEmailAndPassword( auth, credentials.email, credentials.password ); if (userCredential.user) { // 根据UID查询用户角色 const userDoc = await getDoc(doc(db, "users", userCredential.user.uid)); const userData = userDoc.data(); // 返回包含角色的用户对象 return { id: userCredential.user.uid, email: userCredential.user.email, role: userData?.role ?? "user" // 无角色时默认设为user }; } return null; } catch (error) { console.error("登录失败:", error); return null; // 必须返回null告知NextAuth登录失败 } }
步骤3:扩展 TypeScript 类型并修正回调
在项目根目录创建 types/next-auth.d.ts 文件,扩展 NextAuth 的默认类型:
import NextAuth from "next-auth"; declare module "next-auth" { interface User { role?: string; } interface Session { user: { role?: string; } & DefaultSession["user"]; } interface JWT { role?: string; } }
然后修正回调逻辑,确保角色正确同步:
callbacks: { async jwt({ token, user }) { // 登录时将用户角色写入token if (user) token.role = user.role; return token; }, async session({ session, token }) { // 将token中的角色同步到session if (session.user) session.user.role = token.role; return session; }, },
步骤4:完整修正代码
import NextAuth from "next-auth" import CredentialsProvider from "next-auth/providers/credentials"; import { signInWithEmailAndPassword } from 'firebase/auth'; import { getFirestore, doc, getDoc } from "firebase/firestore"; import auth from '@/app/lib/auth'; const db = getFirestore(); export const authOptions = { secret: process.env.AUTH_SECRET, pages: { signIn: '/signin' }, session: { strategy: "jwt" as const, maxAge: 3600, }, providers: [ CredentialsProvider({ name: 'Credentials', credentials: { email: { label: "Email", type: "email" }, password: { label: "Password", type: "password" } }, async authorize(credentials): Promise<any> { try { if (!credentials?.email || !credentials?.password) return null; const userCredential = await signInWithEmailAndPassword( auth, credentials.email, credentials.password ); if (userCredential.user) { const userDoc = await getDoc(doc(db, "users", userCredential.user.uid)); return { id: userCredential.user.uid, email: userCredential.user.email, role: userDoc.data()?.role ?? "user" }; } return null; } catch (error) { console.error("登录失败:", error); return null; } } }) ], callbacks: { async jwt({ token, user }) { if (user) token.role = user.role; return token; }, async session({ session, token }) { if (session.user) session.user.role = token.role; return session; }, }, } const handler = NextAuth(authOptions) export { handler as GET, handler as POST}
额外注意事项
- 配置 Firestore 规则,允许已登录用户查询自己的文档:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { allow read: if request.auth != null && request.auth.uid == userId; } } } - 注册用户时需将角色写入 Firestore 的
users/{uid}文档,比如新用户默认设为role: "user",管理员角色手动配置。
内容的提问来源于stack exchange,提问作者Adhishtanaka Kulasooriya

