【问题标题】:Problem with Firebase realtime database rules after migrating to Firebase Admin SDK迁移到 Firebase Admin SDK 后 Firebase 实时数据库规则出现问题
【发布时间】:2021-08-22 10:24:56
【问题描述】:

我有一个 2014 年的网络应用程序,它仍在使用 firebase-token-generator 在后端生成自定义身份验证令牌,并在客户端生成 Firebase JS SDK 2.x。

这是一个开源项目,但没有人可以自己托管它,因为当创建一个新的 Firebase 项目时,不再生成已弃用的firebase-token-generator 库工作所需的令牌。

我决定将它迁移到 Firebase Admin SDK 和最新版本的 JS SDK。我认为它进展顺利,我没有收到任何错误,但看起来一些以前运行良好的规则,不再按预期工作。以下是数据库规则:

{
  "rules": {
    "rooms": {
      "$roomid": {
        // You can see people in the room only if you are in it as well
        ".read": "auth != null && data.child('users').hasChild(auth.uid)",
        "users": {
          "$uid": {
            // You can modify only your own info
            ".write": "auth != null && $uid == auth.uid",
          }
        }
      }
    }
  }
}

这是一个简化的客户端代码:

      const firebaseConfig = {...};
      firebase.initializeApp(firebaseConfig);

      // Get custom auth token from the server
      const response =  await fetch('/auth');
      const data = await response.json();

      const auth = window.firebase.auth();
      await auth.signInWithCustomToken(data.token);

      const user = { uuid: data.id };
      const roomName = 'firebase-test'

      const database = window.firebase.database();
      const connectionRef = database.ref('.info/connected');
      const roomRef = database.ref(`rooms/${roomName}`);
      const usersRef = roomRef.child('users');
      const userRef = usersRef.child(data.id);

      window.users = {};

      connectionRef.on('value', (connectionSnapshot) => {
        if (connectionSnapshot.val() === true) {
          // Remove yourself from the room when disconnected
          userRef.onDisconnect().remove();

          // Add yourself to the room
          userRef.set(user);

          // Watch for added users
          usersRef.on('child_added', (userAddedSnapshot) => {
            const addedUser = userAddedSnapshot.val();
            window.users[addedUser.uuid] = addedUser;
          });

          // Watch for removed users
          usersRef.on('child_removed', (userRemovedSnapshot) => {
            const removedUser = userRemovedSnapshot.val();
            delete window.users[removedUser.uuid];
          });
        }
      });
    }

和服务器代码:

app.get('/auth', async (req, res, next) => {
  const uid = uuidv4();
  const token = await firebaseAdmin.auth().createCustomToken(uid);
  res.json({ id: uid, token });
});

现在,我没有收到任何错误,但是例如当用户关闭或重新加载页面时,有时它们不再从数据库中删除。有时,用户不会收到有关添加的新用户的更新。我尝试使用 Firebase 模拟器对其进行调试,但我只能发现数据库规则中的 $uid == auth.uid 之类的检查有时会开始失败,即使它们在迁移之前运行良好。

我创建了 2 个演示来说明这个问题。这是使用旧 SDK 的版本的链接:https://codesandbox.io/s/firebase-issue-test-old-sdk-loukt,这是使用新 SDK 的版本的链接:https://codesandbox.io/s/firebase-issue-test-new-sdk-c9lwh。如果您在 2 个选项卡中打开后者,然后重新加载一个选项卡,然后重新加载另一个选项卡,然后再次重新加载第一个选项卡,依此类推,您会看到用户没有被删除。使用旧 SDK 的版本按预期工作。两个版本的客户端代码几乎完全相同。

有谁知道这段代码可能有什么问题以及如何确保从数据库中正确删除关闭页面的用户?

【问题讨论】:

    标签: javascript firebase-realtime-database firebase-authentication firebase-security


    【解决方案1】:

    当调用onDisconnect 处理程序时,听起来用户可能不再登录。您可能希望在代码中单独捕获它,本质上允许未经身份验证的删除:

    ".write": "(auth != null && $uid == auth.uid) ||
               (auth == null && !newData.exists())",
    

    请注意,您的代码仅使用单个属性来跟踪用户的连接,这会在用户设备断开连接的情况下导致误报(用户被标记为不在线,而实际上他们是在线的) -并重新连接。一个常见的场景:

    1. 设备已连接,并已写入其userRef
    2. 设备失去连接,例如用户进入隧道。这就是所谓的脏断开连接,在这种情况下,服务器需要几分钟才能检测到客户端已消失。
    3. 设备创建新连接,例如用户从隧道中出来。这个新连接再次写入userRef
    4. 服务器检测到原来的连接断开,删除userRef处的数据。

    现在您在userRef 上没有用户的在线信息,尽管设备已连接。

    以上是最常见的情况,但其实还有更多类似的情况。出于这个原因,最好允许跟踪每个用户的多个连接,如presence system in the documentation 所示。

    这里的逻辑是,用户只有在其节点存在时才存在,无论其下存在多少连接节点。

    【讨论】:

    • 谢谢!我添加了您建议的规则,但没有帮助。这里发生了一些非常奇怪的事情。假设数据库是空的,我在选项卡 A 中打开此页面。我可以看到已添加用户,如果我继续重新加载页面,则用户将被删除并正确添加。当我在另一个选项卡 B 中打开此页面时,我可以正确看到 2 个用户。我可以继续重新加载此选项卡 B 并始终看到 2 个用户。但是,当我此时重新加载选项卡 A 时,我得到 3 个用户,因为第一个用户没有被删除。如果我现在重新加载选项卡 B,我会获得 4 个用户,依此类推...
    • 嗯....我不确定那里发生了什么。你能在 jsbin 或 stackblitz 之类的网站上设置 repro 吗?
    • 谢谢!我在 CodeSandbox 上创建了 2 个项目 - 一个使用新 SDK,一个使用旧 SDK,它们都使用相同的安全规则连接到同一个 Firebase 实时数据库。由于这两个项目都包含一个服务器和一个 Firebase 服务帐户私钥,我可以向您发送这两个项目的链接吗?在推特上?
    • 嘿弗兰克!这是使用旧 SDK 的版本的链接:codesandbox.io/s/firebase-issue-test-old-sdk-loukt,这是使用新 SDK 的版本:codesandbox.io/s/firebase-issue-test-new-sdk-c9lwh 如果您在 2 个选项卡中打开后者,然后重新加载一个选项卡,然后重新加载另一个选项卡,然后重新加载第一个选项卡再等等,你会看到用户没有被删除。使用旧 SDK 的版本按预期工作。两个版本的客户端代码几乎完全相同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-27
    • 2021-07-01
    • 1970-01-01
    • 2021-01-31
    相关资源
    最近更新 更多