从 Cloud Functions for Firebase 中的 Firestore 触发器获取用户 ID?
Posted
技术标签:
【中文标题】从 Cloud Functions for Firebase 中的 Firestore 触发器获取用户 ID?【英文标题】:Getting the user id from a Firestore Trigger in Cloud Functions for Firebase? 【发布时间】:2018-04-05 08:20:29 【问题描述】:在下面的示例中,有没有办法获取写信给“offers/offerId”的用户的用户 ID (uid)?我尝试按照here 的描述进行操作,但它在 Firestore 中不起作用。
exports.onNewOffer = functions.firestore
.document('offers/offerId')
.onCreate(event =>
...
);
【问题讨论】:
【参考方案1】:我为此苦苦挣扎了一段时间,最后联系了 Firebase 支持:
event.auth.uid
在 firestore 数据库触发器的事件对象中未定义。 (它适用于实时数据库触发器)
当我console.log(event)
时,我在输出中找不到任何auth
。
官方支持回答:
抱歉,尚未在 Firestore SDK 中添加身份验证。我们将其列在下一个功能中。
请留意我们的发行说明以获取任何进一步的更新。
我希望这可以为某人节省几个小时。
更新:
问题已关闭,该功能将永远不会实现:
大家好 - 另一个更新。不幸的是,由于技术限制,将不会实现对 Firestore 触发器的 context.auth 的原生支持。但是,有一个不同的解决方案有望满足您的用例,但我不能分享细节。在这个论坛上,我们通常只开放可以在函数 SDK 本身内部解决的问题——我一直开放这个,因为它看起来很重要,我想提供一些关于跟踪这项工作的内部错误的更新。既然已经做出决定,我将关闭它。再次感谢大家的耐心等待,很抱歉没有更好的消息。请使用here 中引用的解决方法。
【讨论】:
有一个未解决的问题:github.com/firebase/firebase-functions/issues/… 嗨,我的云函数端点看起来不同,并且有 export const getUsersInThisChatRoom = functions.https.onRequest(async function(req,res) 没有 req.auth.uid 也没有事件。让uid = admin.auth().getUser(req.auth.uid); 我缺少什么? 我看到你有一个 if 语句来避免循环。但是如果客户端手动添加 createdAt 字段呢?我认为这张支票不应该在那里。如果我错了,请纠正我。但我相信您想在服务器上添加时间戳并避免客户端添加它 身份验证对象永远不会为 Firestore 实现 :( github.com/firebase/firebase-functions/issues/…【参考方案2】:我如何解决这个问题的总结/一个可行的解决方案:
在客户端
将登录/当前用户的 uid(例如 creatorId
)添加到他们正在创建的实体中。通过将firebase.auth().onAuthStateChanged()
User 对象存储在您的应用状态中来访问此 uid。
在 Firebase Firestore/数据库中
向create
添加安全规则,以验证客户端提供的creatorId
值是否与经过身份验证的用户的uid 相同;现在您知道客户端没有欺骗 creatorId
并且可以在其他地方信任此值。
例如
match /entity/entityId
allow create: if madeBySelf();
function madeBySelf()
return request.auth.uid == request.resource.data.creatorId;
在 Firebase 函数中
将onCreate
触发器添加到您创建的实体类型,以使用客户端提供且现已验证的creatorId
来查找创建用户的个人资料信息,并将此信息关联/附加到新实体文档。
这可以通过以下方式完成:
在创建新帐户时创建users
集合和单独的user
文档,并使用应用程序有用的字段(例如displayName
)填充新的user
文档。这是必需的,因为 Firebase 身份验证系统公开的字段不足以供消费者应用程序使用(例如,displayName
和 avatarURL
未公开),因此您不能仅仅依靠以这种方式查找创建用户的信息。
例如(使用 ES6)
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
const APP = admin.initializeApp()
export const createUserRecord = functions.auth.user()
.onCreate(async (userRecord, context) =>
const userDoc =
id: userRecord.uid,
displayName: userRecord.displayName || "No Name",
avatarURL: userRecord.photoURL || '',
return APP.firestore().collection('users').doc(userRecord.uid).set(userDoc)
)
-
现在您已经拥有经过验证的
creatorId
值和有用的user
对象,将onCreate
触发器添加到您的实体类型(或您创建的所有实体)以查找创建用户的信息并将其附加到创建的对象。
export const addCreatorToDatabaseEntry = functions.firestore
.document('<your entity type here>/entityId')
.onCreate(async (snapshot, context) =>
const userDoc = await APP.firestore().collection('users').doc(snapshot.data().creatorId).get()
return snapshot.ref.set( creator: userDoc.data() , merge: true )
)
这显然会导致整个系统中出现大量重复的用户信息数据——你可以做一些清理工作(在上面的实现中,'creatorId` 在创建的实体上重复)——但现在它是超级的很容易显示谁在整个应用中创建了什么,并且似乎是“Firebase 方式”。
希望这会有所帮助。我发现 Firebase 在某些方面非常令人惊叹,并且让一些通常容易的事情(比如这样)变得比他们“应该”更难;总的来说,虽然我是一个主要的粉丝。
【讨论】:
【参考方案3】:在将其添加到 firestore 函数之前,一种解决方法是在创建文档时将 user_id
添加为字段,然后在之后删除。然后,您可以在 onCreate 函数中获取它,然后在将其用于您需要的用途后,在仍在该函数中时,只需从该文档中删除该字段即可。
【讨论】:
当然,如果您想跟踪更新,您可以拥有一个updatedBy
字段,该字段始终由客户端在更新文档时设置。但是,如果您想跟踪文档删除,这无济于事。您必须想出更难看的解决方法,也许您有一个单独的集合,您可以将已删除的文档移入其中,或者从不实际删除文档,而是将 deleted
属性设置为 true。【参考方案4】:
documentation 明确指出 context.auth
参数仅在实时数据库中可用。
此字段仅为实时数据库触发器和 可调用函数。对于未经身份验证的用户,此字段为空。 对于不提供用户的 Firebase 管理员用户和事件类型 信息,该字段不存在。
我个人意识到我的数据路径中已经有了 userId。
export const onCreate = functions.firestore.document('docs/userId/docs/docId')
.onCreate((snapshot, context) =>
const userId = context.params.userId;
【讨论】:
【参考方案5】:正如上面已经建议的,解决方法是添加一个包含数据本身的 user_id 字段,然后在服务器上读取它。
这种方法的缺点是存在安全漏洞。由于我们没有在服务器上验证用户 ID,因此任何其他用户都可以通过将其 ID 与数据一起发送来冒充其他用户。
对于安全关键应用程序,解决方案是使用安全规则来验证正确的 user_id 已与数据一起发送
allow write: if resource.data.user_id == request.auth.uid;
【讨论】:
正确的规则应该检查 request.resource 而不是资源(更改前的值)allow write: if request.resource.data.user_id == request.auth.uid;
【参考方案6】:
您可以通过 Callable 函数添加您的数据,从中您可以访问当前用户 ID:
exports.addNewOffer = functions.https.onCall(async (data, context) =>
const uid = context.auth.uid
const writeResult = await admin.firestore().collection('offers').add( ... )
...
return id: writeResult.id, ...
)
【讨论】:
【参考方案7】:snap._fieldsProto.uid.stringValue
是怎么回事
示例:
exports.hello = functions.firestore.document('hello/worldId').onCreate((snap, context) =>
console.log(snap._fieldsProto.uid.stringValue)
);
【讨论】:
我得到同样的错误。这行不通!TypeError: Cannot read properties of undefined (reading 'uid')
【参考方案8】:
这应该可以解决问题:
exports.newMessage = functions.firestore
.document('groups/messages/messageId')
.onCreate((snap, context) =>
var fromId = snap.data().fromId;
【讨论】:
您的函数应始终指向文档级别,而不是指向集合级别【参考方案9】:您可以通过在用户上调用getIdToken()获取当前登录用户tokenId:https://firebase.google.com/docs/reference/functions/functions.auth.UserInfo
【讨论】:
但是这种方法只在 auth 函数上可用,不是吗? 我认为你可以使用 firebase.auth().currentUser.uid 这是验证用户 ID 并从 firebase 获取用户 ID 的方法。 firebase.google.com/docs/auth/admin/verify-id-tokens以上是关于从 Cloud Functions for Firebase 中的 Firestore 触发器获取用户 ID?的主要内容,如果未能解决你的问题,请参考以下文章
从 Cloud Functions for Firebase 中的 Firestore 触发器获取用户 ID?
从 Cloud Functions for Firebase 获取当前用户 ID(PubSub 触发器)
具有 Cloud Functions for Firebase 的自定义观察者
Cloud Functions for Firebase:无法访问 BigQuery