Next.js 14服务端组件结合Firebase Auth调用Firestore权限被拒
问题分析与解决方案
核心问题:你在Next.js 14 SSR中使用Firebase Session Cookie认证,但客户端Firestore SDK发起请求时并未携带有效认证信息,导致Firestore安全规则触发权限拒绝。客户端auth.currentUser为undefined,是因为Firebase客户端Auth不会自动读取Session Cookie,必须手动完成认证流程。
解决方案1:服务端使用Firebase Admin SDK操作Firestore
Admin SDK在服务端运行,拥有最高权限,无需通过客户端安全规则校验,适合SSR场景下的服务端数据获取。
步骤:
- 初始化Firebase Admin App(确保只初始化一次)
- 验证Session Cookie获取用户UID
- 使用Admin SDK查询Firestore
import { cookies } from 'next/headers'; import { getAuth } from 'firebase-admin/auth'; import { getFirestore } from 'firebase-admin/firestore'; import { initializeApp, getApps } from 'firebase-admin/app'; // 初始化Admin App(避免重复初始化) if (!getApps().length) { initializeApp({ // 填入你的Firebase Admin配置(从控制台获取) }); } // 验证Session Cookie的工具函数 async function getAuthenticatedUid() { const sessionCookie = cookies().get('session')?.value; if (!sessionCookie) return null; try { // 验证Cookie有效性,开启checkRevoked确保Cookie未被撤销 const decodedClaims = await getAuth().verifySessionCookie(sessionCookie, true); return decodedClaims.uid; } catch (err) { // Cookie无效/过期,可在此处处理跳转登录等逻辑 return null; } } const AdminPage: React.FC = async () => { const fetchCount = async () => { const uid = await getAuthenticatedUid(); if (!uid) throw new Error('用户未登录'); // 使用Admin SDK的Firestore实例查询 const db = getFirestore(); const coll = db.collection('test_results'); const snapshot = await coll.count().get(); return snapshot.data().count; }; const count = await fetchCount(); return ( <div> <p>COUNT : {count}</p> </div> ); }; export const dynamic = 'force-dynamic'; export default AdminPage;
解决方案2:为客户端Firestore SDK注入认证凭证
如果需要保留客户端Firestore SDK的使用(比如后续客户端交互需要),可在服务端验证Session Cookie后,通过自定义Token登录客户端Auth实例,让Firestore请求携带认证信息。
import { cookies } from 'next/headers'; import { getAuth as getAdminAuth } from 'firebase-admin/auth'; import { initializeApp, getApps } from 'firebase-admin/app'; import { getAuth, signInWithCustomToken } from 'firebase/auth'; import { getFirestore, collection, getCountFromServer } from 'firebase/firestore'; // 初始化Admin App if (!getApps().length) { initializeApp({ /* Admin配置 */ }); } // 客户端Firebase配置 const clientFirebaseConfig = { /* 你的客户端Firebase配置 */ }; const clientApp = getApps().find(app => app.name === '[DEFAULT]') || initializeApp(clientFirebaseConfig); const AdminPage: React.FC = async () => { const fetchCount = async () => { const sessionCookie = cookies().get('session')?.value; if (!sessionCookie) throw new Error('用户未登录'); // 验证Session Cookie const decodedClaims = await getAdminAuth().verifySessionCookie(sessionCookie, true); // 生成自定义Token const customToken = await getAdminAuth().createCustomToken(decodedClaims.uid); // 在服务端登录客户端Auth实例 const clientAuth = getAuth(clientApp); await signInWithCustomToken(clientAuth, customToken); // 此时Firestore请求会携带认证信息 const db = getFirestore(clientApp); const coll = collection(db, 'test_results'); const snapshot = await getCountFromServer(coll); return snapshot.data().count; }; const count = await fetchCount(); return ( <div> <p>COUNT : {count}</p> </div> ); }; export const dynamic = 'force-dynamic'; export default AdminPage;
关键说明
- Session Cookie是给服务端验证用的,客户端Firebase Auth不会自动识别它,必须通过
signInWithCustomToken等方法手动初始化登录状态,才会生成currentUser。 - 使用Admin SDK时,无需受Firestore安全规则限制(Admin拥有完全权限),如果需要严格遵循规则,建议使用解决方案2。
内容的提问来源于stack exchange,提问作者esafresa
相关产品推荐
相关产品推荐

