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

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到服务器验证

  1. 客户端调用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 })
  });
};
  1. 服务器端验证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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 01:12:13