如果用户存在,则仅使用 Google Auth Provider 进行身份验证

Posted

技术标签:

【中文标题】如果用户存在,则仅使用 Google Auth Provider 进行身份验证【英文标题】:Only authenticate with Google Auth Provider if user exists 【发布时间】:2019-03-01 14:45:56 【问题描述】:

我正在使用 Firebase 通过 GoogleAuthProvider 对我们应用中的用户进行身份验证。但如果新用户还不是经过身份验证的用户,我不希望他们登录。

如果用户存在,则将其登录并console.log('user ' + user.email + ' does exist!');

但是,如果用户不存在。然后不允许认证和console.log('user ' + user.email + ' does not exist!')

var googleProvider = new firebase.auth.GoogleAuthProvider();
export const doSignInWithGoogle = () => auth.signInWithPopup(googleProvider);

googleLogin = () => 
    auth
      .doSignInWithGoogle()
      .then(result => 
        var user = result.user;
        const userRef = db.collection('users').doc(user.uid);
        userRef.get().then(docSnapshot => 
          if (docSnapshot.exists) 
            userRef.onSnapshot(() => 
              console.log('user ' + user.email + ' does exist!');
            );
           else 
            console.log('user ' + user.email + ' does not exist!');
          
        );
      )
      .catch(error => 
        this.setState(updateByPropertyName('error', error));
      );
  ;

我认为在 Firestore 中引用用户记录是一种简单的方法。但是,也许 Firebase Auth 已经有办法做到这一点。我找不到文档或任何示例。

在上面的代码中,没有任何内容被记录,用户要么被创建,要么被登录。

如何阻止新用户注册,同时仍允许现有用户登录?

【问题讨论】:

您能否在userRef.get().then 的处理程序中查看docSnapshot 是否不是undefined 这与您的问题相似吗? ***.com/questions/38357554/… 嘿@Josh。不幸的是没有。任何对logging 上述代码任何部分的尝试都将被忽略。就好像doSignInWithGoogle 之后的promise 被忽略了。我知道基于在其他场景中的使用,userRef.get().then 将返回结果。 谢谢@MaximillianLaumeister。是的,类似。然而,我只想通过 doSignInWithGoogle 在单个组件中限制注册,而不是整个应用程序。我通过不同的入职流程处理注册。 【参考方案1】:

使用Firebase 安全规则,只能检查密钥是否存在 - 因此不能在用户表中搜索:

"emails": 
    "example1@gmail.com": true,
    "example2@gmail.com": true

然后可以检查安全规则,如果auth.token.email 存在作为密钥


    "rules": 
        ".read": "root.child('emails').child(auth.token.email).exists(),
        ".write": false,
    

在客户端,这应该会引发"The read failed: Permission denied error" 错误,然后进行相应处理。连接到 Firebase 注册是不可能的——但是虽然他们无法登录,但这也有同样的努力(除了 on 必须不时清理用户数据库);例如。使用 Cloud Functions 删除在emails“表”中没有电子邮件作为键的用户。

Firestore 安全规则中,可以检查:

request.auth.token.email & request.auth.token.email_verified

例如,有一个名为 emails 的集合和一个名为 content 的集合:

match /databases/database/documents 

    function userMatchesId(userId) 
        return request.auth != null && request.auth.uid == userId
    
    function readAllowed(email) 
        return if get(/databases/$(database)/documents/emails/$(request.auth.token.email)).data != null
    

    match /users/userId 
        allow get: if userMatchesId(userId)
    
    match /content 
        allow get: if readAllowed(request.auth.token.email)
    

【讨论】:

谢谢马丁。我看过这个场景,但它会控制整个应用程序。然而,我只是希望在登录页面上将登录限制为当前用户,而在注册页面上将允许注册。设置安全规则时可以这样做吗?我可以将这些规则应用于 URL 或路径名吗? 是否可以像上面那样检查密钥是否存在。因此.doSignInWithGoogle().then(result => //check if key exists? @Darren 有一个示例结构,就像我的第一个示例一样......我可以展示如何构建它。当然这应该配置为登录成功后可用内容的范围。 谢谢,那太好了。我只是在 Firebase 文档中阅读您的答案。【参考方案2】:

如果你真的想使用signInWithPopup方法,你有这个选项, 但这不是最好的方法。当您使用 google 登录时,signInWithPopup 方法会返回一个承诺。您可以从结果对象访问additionalUserInfo 中的isNewUser 属性。然后删除你刚刚创建的用户。

firebase.auth().signInWithPopup(provider).then(
     function (result) 
          var token = result.credential.accessToken;
          var user = result.user;

          //this is what you need
          var isNewUser = result.additionalUserInfo.isNewUser;
          if (isNewUser) 
               //delete the created user
               result.user.delete();
           else 
               // your sign in flow
               console.log('user ' + user.email + ' does exist!');
          
     ).catch(function (error) 
     // Handle Errors here.

);

这是一种简单的方法,但在创建后删除并不是最佳做法。还有一个选择,

您可以为此使用signInAndRetrieveDataWithCredential 方法。根据docs,

auth/user-not-found 将是 如果使用来自的凭据登录,则抛出 firebase.auth.EmailAuthProvider.credential 并且没有用户 对应于给定的电子邮件。

function googleSignInWithCredentials(id_token) 
     // Build Firebase credential with the Google ID token.
     var credential = firebase.auth.GoogleAuthProvider.credential(id_token);

     // Sign in with credential from the Google user.

     firebase.auth().signInAndRetrieveDataWithCredential(credential)
          .then(function (userCredential) 
               //sign in
               console.log(userCredential.additionalUserInfo.username);
          ).catch(function (error) 
               // Handle Errors here.
               var errorCode = error.code;
               if (errorCode === 'auth/user-not-found') 
                    //handle this
                else 
                    console.error(error);
               
          );

here 是来自 firebase github repo 的示例。

【讨论】:

谢谢 - 第二个选项进行了一些调整,非常适合场景。 太棒了..!很高兴我能帮助你。 :) 安卓有没有类似signInAndRetrieveDataWithCredential的方法?【参考方案3】:

您在登录后从 firebase 收到的对象具有 additionalUserInfo,您在其中拥有属性 isNewUser

你可以在这里找到参考:https://firebase.google.com/docs/reference/js/firebase.auth.html#.AdditionalUserInfo

【讨论】:

以上是关于如果用户存在,则仅使用 Google Auth Provider 进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章

仅当用户存在于 Firebase 中时 Flutter google 登录?

PassportJs Google Auth 将现有用户保存为数据库中的新用户。我该如何解决?

如何确定用户是不是关闭了 google auth2.signIn() 窗口?

Django模板:如果用户属于多个组,则仅显示一次链接

如果不在另一个表中,则仅从一个表中选择用户

如何使用 Google Auth JWT 授权登录用户? (图片)