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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 02:15:40