Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Only authenticate with Google Auth Provider if user exists

I am using Firebase to authenticate users in our app using GoogleAuthProvider. But I don't want a new user to sign in if they are not already an authenticated user.

If the user exists then sign them in and console.log('user ' + user.email + ' does exist!');.

However, if the user does not exist. Then do not allow authentication and 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));
      });
  };

I thought referencing the user records in Firestore would be a simple approach to this. However, perhaps Firebase Auth already have a way to do this. I cannot find documentation or any example.

In the above code, nothing gets logged and the user is either created or logged in.

How can I stop new users from signing up, whilst still allowing current users to sign in?

like image 930
Darren Avatar asked Sep 25 '18 22:09

Darren


3 Answers

If you really want to use signInWithPopup method, you have this option, but it's not the best way. when you are signing in with google, signInWithPopup method returns a promise. you can access the isNewUser property in additionalUserInfo from resulting object. then delete the user you just created.

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.

});

This is the easy way but deleting after creating is not the best practice. There is another option,

you can use, signInAndRetrieveDataWithCredential method for this. according to the docs,

auth/user-not-found will be Thrown if signing in with a credential from firebase.auth.EmailAuthProvider.credential and there is no user corresponding to the given email.

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 is an example from firebase github repo.

like image 74
Sankha Karunasekara Avatar answered Oct 24 '22 21:10

Sankha Karunasekara


The object you receive from firebase after login has additionalUserInfo where you have the property isNewUser.

You can find the reference here: https://firebase.google.com/docs/reference/js/firebase.auth.html#.AdditionalUserInfo

like image 31
Raphael Jenni Avatar answered Oct 24 '22 20:10

Raphael Jenni


with Firebase security rules, can only check if keys exist - therefore searching in the users table is not an option:

"emails": {
    "[email protected]": true,
    "[email protected]": true
}

and then one can check with security rules, if the auth.token.email exists as a key:

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

in the client, this should throw an "The read failed: Permission denied error" error then, to be handled accordingly. hooking into the Firebase sign-up isn't possible - but while they cannot log-in, this has the same effort (except that on has to clean up the user-database from time to time); eg. with a Cloud Function, which deletes users, which do not have their email as key in the emails "table".

in Firestore security rules, one can check with:

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

for example, with a collection called emails and a collection called 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)
    }
}
like image 40
Martin Zeitler Avatar answered Oct 24 '22 21:10

Martin Zeitler