【问题标题】:Firestore token doesn't update in security rules after setting custom claim设置自定义声明后,Firestore 令牌不会在安全规则中更新
【发布时间】:2021-04-25 14:03:48
【问题描述】:

TLDR;

  1. 根据自定义声明设置 Firestore 安全规则。
  2. Cloud Firestore 用户是通过手机身份验证创建的。
  3. 云功能在创建用户时触发并添加自定义声明角色 - 管理员。在实时数据库中更新条目以指示索赔更新。
  4. 在客户端收听实时数据库的更新,并在自定义声明更新后致电user.getIdToken(true);
  5. 能够在代码中看到添加的自定义声明。
  6. 由于缺少权限(自定义声明),无法读取 Firestore 中的文档。
  7. 刷新浏览器页面,现在可以阅读文档了。

我有一个云功能,可以在用户创建时添加自定义声明 role - admin

exports.processSignUp = functions.auth.user().onCreate((user) => {
    const customClaims = {
      role: 'admin',
    };
    // Set custom user claims on this newly created user.
    return admin.auth().setCustomUserClaims(user.uid, customClaims)
      .then(() => {
        // Update real-time database to notify client to force refresh.
        const metadataRef = admin.database().ref("metadata/" + user.uid);
        // Set the refresh time to the current UTC timestamp.
        // This will be captured on the client to force a token refresh.
        return metadataRef.set({refreshTime: new Date().getTime()});
      })
      .catch(error => {
        console.log(error);
      });
});

我在实时数据库中侦听更改事件,以检测用户自定义声明的更新。

let callback = null;
let metadataRef = null;
firebase.auth().onAuthStateChanged(user => {
  // Remove previous listener.
  if (callback) {
    metadataRef.off('value', callback);
  }
  // On user login add new listener.
  if (user) {
    // Check if refresh is required.
    metadataRef = firebase.database().ref('metadata/' + user.uid + '/refreshTime');
    callback = (snapshot) => {
      // Force refresh to pick up the latest custom claims changes.
      // Note this is always triggered on first call. Further optimization could be
      // added to avoid the initial trigger when the token is issued and already contains
      // the latest claims.
      user.getIdToken(true);
    };
    // Subscribe new listener to changes on that node.
    metadataRef.on('value', callback);
  }
});

我在 Cloud Firestore 上有以下安全规则。

service cloud.firestore {
  match /databases/{database}/documents {
    match /{role}/{document=**} {
        allow read: if request.auth != null &&
                        request.auth.token.role == role;
    }
  }
}

创建用户后,我的云函数触发并添加自定义声明role = admin

user.getIdToken(true); 的结果是令牌在我的客户端上刷新,我可以看到设置的自定义声明。

当我尝试获取用户应该能够阅读的文档时,我获得了云防火墙安全规则拒绝的权限。

当我刷新浏览器页面时,我可以读取路径中的文档。

我希望能够访问 firebase 文档而无需刷新浏览器。有这种可能吗?

有人可以告诉我我的方法/期望有什么问题吗?

【问题讨论】:

    标签: firebase google-cloud-firestore firebase-security


    【解决方案1】:

    传播令牌所需的时间可能比您想象的要长。我广泛使用自定义声明 - 我发现可以设置 .onIdTokenChanged() 来跟踪 uid 和令牌更新,然后然后显式调用 .getIdTokenResult(true) 来更新我的本地令牌。只有在两者都完成后,您才能对 Firestore 和/或 RTDB 进行 customClaim 安全调用。

    【讨论】:

    • 我会添加 - 最近 Firestore 的一些更新似乎直接解决了这个问题。我没有尝试过,因为这里的解决方案对我有用。
    • onIdTokenChanged() 并打电话给.getIdTokenResult(true) 不幸的是,在我的情况下不起作用。我在.getIdTokenResult(true).then 中看到了声明,但是当我尝试读取.getIdTokenResult(true).then 中的文档时,我仍然收到Missing or insufficient permission 错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-19
    • 1970-01-01
    • 1970-01-01
    • 2020-06-19
    • 2018-07-23
    • 2018-03-27
    • 2020-10-11
    相关资源
    最近更新 更多