从 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 身份验证系统公开的字段不足以供消费者应用程序使用(例如,displayNameavatarURL 未公开),因此您不能仅仅依靠以这种方式查找创建用户的信息。

    例如(使用 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

Cloud Functions for Firebase - 检索已部署的代码? [复制]

Cloud Functions for Firebase:在不触及最大超时的情况下完成长流程