Next.js中Firebase/Firestore认证请求权限不足问题求助
Next.js中Firebase/Firestore认证请求权限不足问题
我在Next.js项目里初始化Firestore后,调用API时用户明明已经登录,但Firestore一直返回Missing or insufficient permissions错误。我的代码和规则如下:
Firebase初始化代码
import { initializeApp } from "firebase/app"; import { getFirestore } from 'firebase/firestore'; import { GoogleAuthProvider, getAuth } from "firebase/auth"; const firebaseConfig = { apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, }; const app = initializeApp(firebaseConfig); // firestore export const db = getFirestore(app); //auth export const provider = new GoogleAuthProvider(); export const auth = getAuth();
Next.js API路由代码
import { db } from "../../../utils/db/init"; import { collection, addDoc, serverTimestamp, setDoc, doc, } from "firebase/firestore"; /** [POST] Add user to database. * req.body requires: * @param email * @returns user */ export default async function addUserHandler(req, res) { try { const data = { ...req.body, createdAt: serverTimestamp(), followers: [], following: [], }; const { id } = await addDoc(collection(db, "users"), data); await setDoc(doc(db, "users", id), { seed: id }, { merge: true }); // add seed for sorting posts (same as id) res.status(200).json({ ...data, id }); } catch (error) { console.error(error); res.status(500).json(error.message); } }
Firestore安全规则
... match /users/{userId}{ allow read, write: if request.auth.uid != null; }
问题原因及解决方法
核心原因
服务器端执行的API路由中,Firebase实例无法获取客户端的登录状态。客户端的认证信息仅存在于浏览器的Auth实例中,服务器端初始化的Firebase是独立的,因此Firestore规则检测不到request.auth字段,触发权限不足错误。
解决方法一:传递客户端ID Token到服务器验证
- 客户端调用API时携带ID Token
// 客户端组件代码 import { getAuth, getIdToken } from "firebase/auth"; const addUser = async (email) => { const auth = getAuth(); const user = auth.currentUser; if (!user) throw new Error("用户未登录"); const idToken = await getIdToken(user); await fetch('/api/addUser', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${idToken}` }, body: JSON.stringify({ email }) }); };
- 服务器端验证Token并执行操作
import { db } from "../../../utils/db/init"; import { collection, addDoc, serverTimestamp, setDoc, doc, } from "firebase/firestore"; import admin from "firebase-admin"; // 初始化Firebase Admin(仅首次运行时初始化) if (!admin.apps.length) { admin.initializeApp({ credential: admin.credential.cert({ projectId: process.env.FIREBASE_PROJECT_ID, clientEmail: process.env.FIREBASE_CLIENT_EMAIL, privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n') }) }); } export default async function addUserHandler(req, res) { try { // 提取并验证ID Token const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ message: '未提供有效认证令牌' }); } const idToken = authHeader.split(' ')[1]; const decodedToken = await admin.auth().verifyIdToken(idToken); // 使用Admin SDK操作Firestore(Admin拥有完全权限,可绕过规则) const adminDb = admin.firestore(); const data = { ...req.body, createdAt: admin.firestore.FieldValue.serverTimestamp(), followers: [], following: [], uid: decodedToken.uid // 关联用户UID }; const docRef = await adminDb.collection('users').add(data); await adminDb.collection('users').doc(docRef.id).set({ seed: docRef.id }, { merge: true }); res.status(200).json({ ...data, id: docRef.id }); } catch (error) { console.error(error); res.status(500).json(error.message); } }
解决方法二:直接在客户端操作Firestore
既然用户已在客户端登录,可直接在客户端调用Firestore方法,此时客户端Auth实例会自动携带认证信息,Firestore规则能正常检测request.auth:
// 客户端组件代码 import { db } from '../utils/db/init'; import { collection, addDoc, serverTimestamp, setDoc, doc } from 'firebase/firestore'; import { getAuth } from 'firebase/auth'; const addUser = async (email) => { const auth = getAuth(); const user = auth.currentUser; if (!user) throw new Error('用户未登录'); const data = { email, createdAt: serverTimestamp(), followers: [], following: [], uid: user.uid }; const { id } = await addDoc(collection(db, "users"), data); await setDoc(doc(db, "users", id), { seed: id }, { merge: true }); return { ...data, id }; };
这种方式无需API路由中转,实现更简单,且能自动满足认证要求。
内容的提问来源于stack exchange,提问作者guckmalmensch
相关产品推荐
相关产品推荐

