使用NextAuth.js对接Firebase Auth时权限拒绝问题排查
request.auth != null失效 我搭建了一个NextJS项目,使用NextAuth.js集成Firebase Auth。按照NextAuth.js文档配置后,只有将Firestore安全规则设置为allow read, write: if true;时才能正常工作,使用allow read, write: if request.auth != null;则无法运行,终端报错权限不足。
相关代码
firebase.config.ts
import { initializeApp, FirebaseOptions } from "firebase/app"; import {getAuth} from "firebase/auth"; const firebaseConfig: FirebaseOptions = { apiKey: process.env.API_KEY, authDomain: process.env.AUTH_DOMAIN, projectId: process.env.PROJECT_ID, storageBucket: process.env.STORAGE_BUCKET, messagingSenderId: process.env.MESSAGING_SENDER_ID, appId: process.env.APP_ID, }; const app = initializeApp(firebaseConfig); const auth = getAuth(app); export {auth, app, firebaseConfig}
/pages/api/auth/[...nextauth].ts
import NextAuth from "next-auth" import GoogleProvider from "next-auth/providers/google" import { FirestoreAdapter } from "@next-auth/firebase-adapter" import {firebaseConfig} from "../../../firebase.config" export default NextAuth({ providers: [ GoogleProvider({ clientId: process.env.GOOGLE_ID, clientSecret: process.env.GOOGLE_SECRET, })], adapter: FirestoreAdapter(firebaseConfig) })
触发认证的组件代码
import { signIn } from "next-auth/react"; export default function Header() { return ( <nav className="flex justify-between items-center p-4 bg-balmy-green"> <div className="flex items-center"> <h1 className="text-3xl font-bold pl-6 text-white">Tixer</h1> </div> <button onClick={() => signIn()} className="hover:text-gray-300 text-white font-bold py-2 px-4 rounded" > Sign In </button> </nav> ); }
终端报错信息
[next-auth][error][adapter_error_getUserByAccount]
Missing or insufficient permissions. {
message: 'Missing or insufficient permissions.',
stack: 'FirebaseError: Missing or insufficient permissions.',
name: 'FirebaseError'
}
原因分析
问题出在NextAuth.js的Firestore适配器是在服务端执行数据库操作,而非前端用户的浏览器请求。当用客户端的firebaseConfig初始化适配器时,服务端的Firestore请求并没有携带用户认证信息(request.auth为null),因此触发了request.auth != null的规则拦截,导致权限不足。
解决方法
需要给服务端的Firestore操作配置Firebase Admin服务账号权限,让NextAuth的适配器能以服务身份访问Firestore,同时保留前端用户的权限控制。
步骤1:下载Firebase服务账号密钥
在Firebase控制台→项目设置→服务账号→点击「生成新的私钥」,下载JSON格式的密钥文件,放在项目根目录(命名为service-account.json),并添加到.gitignore里避免提交。
步骤2:安装Firebase Admin依赖
npm install firebase-admin
步骤3:修改NextAuth配置文件
修改/pages/api/auth/[...nextauth].ts,用Firebase Admin SDK初始化Firestore,替代原有的客户端配置:
import NextAuth from "next-auth" import GoogleProvider from "next-auth/providers/google" import { FirestoreAdapter } from "@next-auth/firebase-adapter" import admin from "firebase-admin" // 初始化Firebase Admin(确保只初始化一次) if (!admin.apps.length) { admin.initializeApp({ credential: admin.credential.cert(require("../../service-account.json")), }) } export default NextAuth({ providers: [ GoogleProvider({ clientId: process.env.GOOGLE_ID!, clientSecret: process.env.GOOGLE_SECRET!, }), ], adapter: FirestoreAdapter(admin.firestore()), // 使用Admin的Firestore实例 })
步骤4:调整Firestore安全规则
修改规则,允许服务账号的操作,同时保留对前端用户的权限验证:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // 允许Firebase服务账号操作NextAuth所需的集合 match /users/{userId} { allow read, write: if request.auth.uid == userId || request.auth.token.firebase.sign_in_provider == "service_account"; } match /accounts/{accountId} { allow read, write: if request.auth.token.firebase.sign_in_provider == "service_account"; } match /sessions/{sessionId} { allow read, write: if request.auth.token.firebase.sign_in_provider == "service_account"; } match /verification_tokens/{tokenId} { allow read, write: if request.auth.token.firebase.sign_in_provider == "service_account"; } // 其他自定义集合可以按需求设置规则 match /your-collection/{docId} { allow read, write: if request.auth != null; } } }
内容的提问来源于stack exchange,提问作者Robert Mihai

