Next.js getServerSideProps访问Firestore权限不足问题求助
Next.js getServerSideProps 调用Firestore 权限问题排查与解决
问题场景
- 当Firestore规则设置为完全开放时,
getServerSideProps可正常获取数据:
match /{document=**} { allow read, write: if true; }
- 将规则改为仅允许已认证用户读写后,触发权限错误:
[Error [FirebaseError]: Missing or insufficient permissions.] { digest: undefined }
- 已通过Firebase Authentication完成前端登录,且尝试等待认证状态变更,问题仍未解决:
const auth = getAuth(); // 等待用户认证状态变更 await new Promise((resolve) => { onAuthStateChanged(auth, (user) => { resolve(user); }); });
核心原因
getServerSideProps运行在服务器端,前端浏览器的用户认证会话(比如登录状态)不会自动同步到服务器端的Firebase客户端实例:
onAuthStateChanged是客户端API,服务器端没有浏览器的Cookie或本地存储,无法监听前端的认证状态变更,导致这段代码在服务器端要么一直挂起,要么返回null。- 服务器端未认证的Firebase客户端请求Firestore时,会被
仅允许认证用户的规则拦截。
解决方案
通过客户端传递认证令牌到服务器端,结合Firebase Admin SDK完成验证与数据查询,具体步骤如下:
1. 前端登录后存储认证令牌到Cookie
登录成功后,获取用户的idToken并写入Cookie,让服务器端能读取到:
import { getAuth } from "firebase/auth"; import Cookies from "js-cookie"; const auth = getAuth(); auth.onAuthStateChanged(async (user) => { if (user) { const idToken = await user.getIdToken(); Cookies.set("firebaseIdToken", idToken, { expires: 1 }); // 有效期1天 } else { Cookies.remove("firebaseIdToken"); } });
2. 初始化Firebase Admin SDK
在项目中创建Admin SDK实例(需提前配置Firebase服务账号密钥):
// lib/firebase-admin.js import admin from "firebase-admin"; if (!admin.apps.length) { admin.initializeApp({ credential: admin.credential.cert(JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT)), }); } export const authAdmin = admin.auth(); export const firestoreAdmin = admin.firestore();
3. 在getServerSideProps中验证令牌并查询数据
从请求Cookie中读取idToken,验证通过后用Admin SDK访问Firestore(Admin SDK不受安全规则限制,也可模拟用户身份):
import { authAdmin, firestoreAdmin } from "../lib/firebase-admin"; export async function getServerSideProps(context) { const { req } = context; const idToken = req.cookies.firebaseIdToken; // 无令牌则跳转登录页 if (!idToken) { return { redirect: { destination: "/login", permanent: false }, }; } try { // 验证令牌有效性 await authAdmin.verifyIdToken(idToken); // 查询Firestore数据 const docSnapshot = await firestoreAdmin.collection("your-collection").doc("target-doc").get(); const data = docSnapshot.data(); return { props: { data } }; } catch (error) { console.error("认证或查询失败:", error); return { redirect: { destination: "/login", permanent: false } }; } }
替代方案:通过API路由中转
若不想直接在getServerSideProps中使用Admin SDK,可创建API路由处理认证与查询,再在getServerSideProps中调用该API:
// pages/api/get-data.js import { authAdmin, firestoreAdmin } from "../../lib/firebase-admin"; export default async function handler(req, res) { const idToken = req.cookies.firebaseIdToken; if (!idToken) return res.status(401).json({ error: "未认证" }); try { await authAdmin.verifyIdToken(idToken); const doc = await firestoreAdmin.collection("your-collection").doc("target-doc").get(); res.status(200).json(doc.data()); } catch (err) { res.status(403).json({ error: "权限不足" }); } }
然后在getServerSideProps中调用:
export async function getServerSideProps(context) { const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/get-data`, { headers: { cookie: context.req.headers.cookie }, }); if (res.status !== 200) { return { redirect: { destination: "/login", permanent: false } }; } const data = await res.json(); return { props: { data } }; }
内容的提问来源于stack exchange,提问作者Andrii Moskalenko
相关产品推荐
相关产品推荐

