Firestore检查登录用户是否验证了他的电子邮件

时间:2020-04-02 16:41:50

标签: javascript google-cloud-firestore firebase-authentication firebase-security angularfire2

因此,我在firestore中有一个集合,该集合只能由已登录并经过验证的用户访问。

这是我保护它的方式:

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    function userIsAdmin() {
      return request.auth.token.accessLevel >= 200;
    }
    function emailVerified() {
      return request.auth.token.firebase.sign_in_provider != 'password'
            || request.auth.token.email_verified == true;
    }
    function isAuthed() {
      return request.auth.uid != null && emailVerified(); // If I remove the && emailVerified() part, it works again in the frontend
    }
    function userIsSameAsResource(userId) {
      return request.auth.uid == userId && emailVerified();
    }

    match /users/{userId} {

      allow get: if isAuthed();
      allow list: if userIsAdmin();

      allow create, delete: if userIsAdmin();
      allow update: if userIsAdmin()
                      || userIsSameAsResource(userId);
    }
  }
}

使用规则模拟器可以按预期工作:

  • Google身份验证提供商,未验证电子邮件:允许阅读
  • Google Auth提供者,已验证电子邮件:允许阅读(但并非真正需要,因为我们不发送电子邮件以外的提供者的验证)
  • 电子邮件身份验证提供程序,未验证电子邮件:阅读不允许
  • 电子邮件身份验证提供者,已验证电子邮件:允许阅读

但是使用前端会发生这种情况:

  • Google身份验证提供商,未验证电子邮件:允许阅读
  • Google Auth提供者,已验证电子邮件:允许阅读
  • 电子邮件身份验证提供程序,未验证电子邮件:阅读不允许
  • 电子邮件身份验证提供者,已验证电子邮件:不允许阅读→失败,并显示Missing or insufficient permissions

以下是我的前端代码中的相关片段:

// currentAuthUserObs() is the current auth user from firebase  (that part works)
// isEmailVerified() is described further down (works too)
// afStorage is AngularFire2's Firestore module

// 'HERE' and 'HERE2' are both printed, so that works as expected. The problem really is the query

this.auth.currentAuthUserObs().subscribe(async (authUser) => {
    console.log('HERE');
    if (authUser != null && await this.auth.emailAuth.isEmailVerified()) {
        console.log('HERE2');
        this.afStorage.doc(`${FirestoreCollections.USERS}/${authUser.uid}`).valueChanges()
            .subscribe(user => {
                console.log(user);
            });
    }
});


// In my case isEmailVerified gets called before and refreshes the currentUser, so it is ok to not call it with `refresh = true` in the subscription above.
public async isEmailVerified(refresh = false): Promise<boolean> {
    if (refresh) {
        await firebase.auth().currentUser.reload();
    }

    const isEmail = () => firebase.auth().currentUser.providerData.some(provider => provider.providerId == 'password');
    return isEmail() ? firebase.auth().currentUser.emailVerified : true;
}

有人知道可能是什么问题吗?

1 个答案:

答案 0 :(得分:0)

正如用户@Kato 所指出的,用户的 ID 令牌需要刷新。您可以使用 firebase.auth().currentUser.getIdToken(true) 来做到这一点 - 在我的示例中看起来像这样:

public async isEmailVerified(refresh = false): Promise<boolean> {
    if (refresh) {
        await firebase.auth().currentUser.reload();
        await firebase.auth().currentUser.getIdToken(true); // <-- here
    }

    const isEmail = () => firebase.auth().currentUser.providerData.some(provider => provider.providerId == 'password');
    return isEmail() ? firebase.auth().currentUser.emailVerified : true;
}