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
相关产品推荐
相关产品推荐

