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

Firestore审计触发器获取用户ID遇context.auth未定义求助

Firestore审计字段记录的可行方案

你的问题核心在于:Cloud Firestore的onWrite触发器无法直接获取触发该操作的客户端用户身份——官方文档里的context.auth仅适用于Auth触发器、Callable函数,或者通过Admin SDK调用时手动传入身份的场景,普通客户端写操作触发的后台函数,Firebase不会传递用户认证信息,所以context.auth必然是undefined。

下面是几种可行的解决思路:

方案1:客户端写入时附带审计字段 + 安全规则校验

这是最轻量化的方案,不需要依赖Cloud Functions,直接让客户端在创建/更新文档时添加审计字段,同时用Firestore安全规则确保字段无法被篡改。

客户端代码示例(Web):

// 创建文档时
const createReferral = async (prod, company, referralData) => {
  const user = firebase.auth().currentUser;
  if (!user) throw new Error('用户未登录');
  
  const referralRef = firebase.firestore()
    .collection(`environment/${prod}/companies/${company}/referrals`)
    .doc();
  
  await referralRef.set({
    ...referralData,
    created_by: user.uid,
    created_at: firebase.firestore.FieldValue.serverTimestamp(),
    updated_by: user.uid,
    updated_at: firebase.firestore.FieldValue.serverTimestamp()
  });
};

// 更新文档时
const updateReferral = async (prod, company, ref, updateData) => {
  const user = firebase.auth().currentUser;
  if (!user) throw new Error('用户未登录');
  
  const referralRef = firebase.firestore()
    .doc(`environment/${prod}/companies/${company}/referrals/${ref}`);
  
  await referralRef.update({
    ...updateData,
    updated_by: user.uid,
    updated_at: firebase.firestore.FieldValue.serverTimestamp()
  });
};

安全规则配置:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /environment/{prod}/companies/{company}/referrals/{ref} {
      // 创建文档时,强制校验创建人、时间字段
      allow create: if request.auth != null &&
        request.resource.data.created_by == request.auth.uid &&
        request.resource.data.created_at == request.time &&
        request.resource.data.updated_by == request.auth.uid &&
        request.resource.data.updated_at == request.time;
      
      // 更新文档时,禁止修改创建人、创建时间,仅允许更新指定字段和更新人、时间
      allow update: if request.auth != null &&
        request.resource.data.created_by == resource.data.created_by &&
        request.resource.data.created_at == resource.data.created_at &&
        request.resource.data.updated_by == request.auth.uid &&
        request.resource.data.updated_at == request.time;
    }
  }
}

方案2:通过Callable函数统一处理文档写入

如果需要更严格的控制(比如不想让客户端直接接触Firestore集合),可以让客户端调用Cloud Callable函数,由函数完成文档写入并添加审计字段——这种场景下,context.auth可以正常获取用户UID。

Cloud Functions代码示例:

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

// 创建推荐文档
exports.createReferral = functions.runWith({ timeoutSeconds: 10, memory: '128MB' })
  .https.onCall(async (data, context) => {
    // 校验用户登录状态
    if (!context.auth) {
      throw new functions.https.HttpsError('unauthenticated', '请先登录');
    }

    const { prod, company, referralData } = data;
    // 生成文档ID
    const referralRef = admin.firestore()
      .collection(`environment/${prod}/companies/${company}/referrals`)
      .doc();

    await referralRef.set({
      ...referralData,
      created_by: context.auth.uid,
      created_at: admin.firestore.FieldValue.serverTimestamp(),
      updated_by: context.auth.uid,
      updated_at: admin.firestore.FieldValue.serverTimestamp()
    });

    return { referralId: referralRef.id };
  });

// 更新推荐文档
exports.updateReferral = functions.runWith({ timeoutSeconds: 10, memory: '128MB' })
  .https.onCall(async (data, context) => {
    if (!context.auth) {
      throw new functions.https.HttpsError('unauthenticated', '请先登录');
    }

    const { prod, company, ref, updateData } = data;
    const referralRef = admin.firestore()
      .doc(`environment/${prod}/companies/${company}/referrals/${ref}`);

    await referralRef.update({
      ...updateData,
      updated_by: context.auth.uid,
      updated_at: admin.firestore.FieldValue.serverTimestamp()
    });

    return { success: true };
  });

安全规则配置(禁止客户端直接操作):

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /environment/{prod}/companies/{company}/referrals/{ref} {
      allow read: if request.auth != null; // 根据需求调整读权限
      allow create, update, delete: if false; // 禁止客户端直接写操作
    }
  }
}

方案3:结合Firebase扩展简化日志存储

Firebase官方提供了Firestore Audit Logs扩展,可以自动记录文档的操作日志(如创建、更新、删除),虽然默认不记录用户UID,但可以结合上述方案1,让客户端附带用户UID,扩展会将该字段一并记录到日志集合中。

这个方案适合需要保留完整操作历史的场景,但需要额外配置扩展并确保客户端字段正确。


内容的提问来源于stack exchange,提问作者Rafael Lima

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 18:24:55