解决Firebase Cloud Functions(Next.js实验性托管)权限问题
核心问题梳理
- 基于Firebase实验性Web框架支持部署Next.js应用,Firebase CLI自动生成了Cloud Function
- 访问需读取Firestore数据的页面时,自动跳转到认证页;完成Google账号认证后返回禁止错误,且自动登出,重试时因已登出触发其他异常
- 尝试给Cloud Function添加
allUsers权限失败,报错:Error: Principals of type allUsers and allAuthenticatedUsers cannot be added to this resource;且本身不想开启该权限(Firestore安全规则已配置仅认证用户可读取数据) - 相同应用在Netlify部署(通过Next.js插件自动创建Edge Functions)时运行正常
问题原因分析
Cloud Function认证上下文丢失
Firebase自动创建的Next.js服务端函数(如nextjs-server)默认可能未配置传递用户认证令牌到Firestore请求,导致Firestore安全规则判定用户未认证,返回403错误,进而触发前端的认证跳转逻辑,甚至引发自动登出。Cloud Function IAM权限限制
自动生成的这类函数属于Firebase托管绑定的特殊资源,不允许直接添加allUsers/allAuthenticatedUsers主体权限,这类请求必须通过Firebase Hosting的路由通道访问,而非直接给函数配置IAM权限。Next.js服务端请求的认证传递问题
在SSR/ISR场景下,前端的认证状态未正确传递到服务端函数,导致服务端访问Firestore时无有效身份凭证。
解决方案步骤
1. 在服务端函数中正确传递并验证用户认证
在Next.js的getServerSideProps或getStaticProps(ISR场景)中,从请求Cookie获取Firebase Auth的ID Token并验证,确保用合法身份访问Firestore:
import { getAuth, verifyIdToken } from "firebase-admin/auth"; import admin from "firebase-admin"; // 初始化Firebase Admin(确保只执行一次) if (!admin.apps.length) { admin.initializeApp(); } export async function getServerSideProps(context) { // 从Cookie获取Firebase Auth默认存储的ID Token const idToken = context.req.cookies.__session; let user = null; if (idToken) { try { // 验证Token合法性 const decodedToken = await verifyIdToken(idToken); user = decodedToken; } catch (error) { // Token验证失败,重定向到认证页 return { redirect: { destination: "/auth", permanent: false, }, }; } } else { // 无Token,重定向到认证页 return { redirect: { destination: "/auth", permanent: false, }, }; } // 用验证后的用户身份访问Firestore const db = admin.firestore(); const dataSnapshot = await db.collection("your-collection") .where("uid", "==", user.uid) .get(); return { props: { data: dataSnapshot.docs.map(doc => doc.data()) } }; }
同时在前端登录后,确保将ID Token写入__session Cookie,以便服务端能获取到:
import { getAuth, onAuthStateChanged } from "firebase/auth"; const auth = getAuth(); onAuthStateChanged(auth, async (user) => { if (user) { const idToken = await user.getIdToken(); // 设置Cookie,确保跨请求传递 document.cookie = `__session=${idToken}; path=/; secure; sameSite=Strict`; } else { // 登出时清除Cookie document.cookie = "__session=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; } });
2. 验证Firebase Hosting重写规则
检查firebase.json中的重写规则,确保所有Next.js请求(包括SSR、Image API)都正确路由到自动创建的函数:
{ "hosting": { "public": "out", "ignore": [ "firebase.json", "**/.*", "**/node_modules/**" ], "rewrites": [ { "source": "**", "function": "nextjs-server" // 替换为你实际的自动生成函数名称,可在Firebase控制台Functions页面查看 } ] } }
3. 检查Firestore安全规则逻辑
确认安全规则正确判断用户认证状态,比如基础的认证用户读写规则:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if request.auth != null; } } }
如果是更细粒度的权限控制(如仅允许用户访问自己的数据),确保request.auth.uid与文档中的uid字段匹配,避免逻辑错误。
4. 排查Cloud Function执行日志
到Firebase控制台的Cloud Functions页面,找到自动生成的函数,查看执行日志,定位具体错误原因(比如ID Token验证失败、Firestore权限拒绝的详细信息)。
5. 升级Firebase CLI并确认实验性功能状态
由于使用的是实验性功能,确保CLI为最新版本并启用了Web框架支持:
# 检查CLI版本 firebase --version # 升级到最新版 npm install -g firebase-tools # 确认实验性功能已启用 firebase experiments:enable webframeworks
内容的提问来源于stack exchange,提问作者Bolaji Ayodeji

