24
votes

WHAT I HAVE

I am using Firebase Authentication in my app where the users can register using Email & Password. If the users have not verified their email, I disable some features until they verify their email.

I also have a button to explicitly trigger verification mail, which just calls, sendEmailVerification(). It works perfectly and verification mail is always sent.

THE PROBLEM

The user gets the verification mails, but when he/she verifies it and comes back to the app, the isEmailVerified() is always false. So my app still doesn't allow the user to use all functions in spite of the fact that he/she has verified their email.

But if they log out and login again, the isEmailVerified() returns true immediately. But is it not good to log out the user and login back again.

Is it a bug in Firebase? Or am I doing something wrong?

6
I think it's a bug. Hopefully, a Firebaser will answer. Related question: stackoverflow.com/a/40967256/4815718.Bob Snyder

6 Answers

12
votes

Hey I know that this is a pretty old thread, but what solved the problem for me FIRAuth.auth()?.currentUser.reload(completion: { (error) in ... })

For anyone that is facing this problem. And I am using the latest Firebase version 10.0.1

UPDATE

Firebase has changed the names of their functions since I've posted my solution.

Please note that all of changes to the current user states can be observed by adding an stateDidChangeListener. Auth.auth()?.currentUser is not updated immediately, so what I did was save the current user to a global variable I created and maintained myself.

Auth.auth().addStateDidChangeListener { (auth, user) in
 // ...
}

Whenever you call Auth.auth()?.currentUser.reload() a refreshed and ready to use user object will be passed through the stateDidChangeListener.

You can then check for any updated data(i.e emailVerified)

4
votes

Here's how I solved this using react-native:

const user = firebase.auth().currentUser;
user.reload().then(() => {
  console.log({emailVerified: user.emailVerified})
})

I coupled this with AppState to run the reload if user is bringing the app from the background to the foreground.

3
votes

Just do this:

FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
user.reload();

if(user.isEmailVerified())
{
     .....
      ...
}
2
votes

You have to reload your Firebase User after you had sent the verification email.

 mAuth.createUserWithEmailAndPassword(inputEmail.getText().toString(), inputPassword.getText().toString())
    .addOnCompleteListener(ResetPasswordActivity.this, task -> {
        user = mAuth.getCurrentUser();user.reload();
        if(mAuth.getCurrentUser().isEmailVerified()){
            next();//intent to next activity
        } else {               
            mAuth.getCurrentUser().sendEmailVerification().addOnCompleteListener(task1 -> System.out.println("function"));
        }
        if (task.isSuccessful()) {
            user = mAuth.getCurrentUser();
            System.out.println(user.isEmailVerified());
        }
    }

That's it!

1
votes

Subscribing to the user auth-state may solve this 'bug'.

constructor(
    public afAuth: AngularFireAuth,
    public router: Router
  ) {
    this.afAuth.authState.subscribe(user => {
      // here you verify if the user is emailVerified BEFORE storing him in local storage.
      // if you want to keep storing the user even
      // before email verification, you need to work out this logic
      if (user && user.emailVerified) {
        this.userData = user;
        localStorage.setItem('user', JSON.stringify(this.userData));
        JSON.parse(localStorage.getItem('user'));
      } else {
        localStorage.setItem('user', null);
        JSON.parse(localStorage.getItem('user'));
      }
    })
  }
0
votes

In my case with react-native-firebase, subscribing to onAuthStateChanged does not update emailVerified. Nor does subscribing to onUserChanged. The only way that works is to call user.reload().

Automatically Trigger user.reload()

However, I don't want user to press another button so that I can trigger user.reload(). I want it triggered automatically. This issue has been discussed here, and it seems that deep linking could allow automatic triggering of user.reload(). But I don't feel like deep linking. Therefore, here is my solution.

After register the user, go to an email verification screen that tells user the system is waiting for user to verify his/her email (shown below). While on this screen, we update the screen every second via setInterval and a dummy state variable. Upon each update, user.reload() is called automatically.

const EmailVerScreen = (props: PropsT) => {
  const [user, setUser] = React.useState();
  const [dummy, setDummy] = React.useState(true);

  React.useEffect(() => {
    const subscriber = auth().onAuthStateChanged(curUser => setUser(curUser));
    return subscriber; // unsubscribe on unmount
  });

  React.useEffect(() => {
    const interval = setInterval(async () => {
      setDummy(!dummy);
      try {
        if (user) {
          await user.reload();
        }
      } catch (error) {
        console.log(error);
      }
    }, 1000);
    return () => clearInterval(interval);
  });

  return (
    <View>
      <Text>{`Dummy is ${dummy ? 'True' : 'False'}`}</Text>
      <Text>
        {user
          ? user.emailVerified
            ? 'Verified'
            : 'Not verified'
          : 'User unavailable'}
      </Text>
    </View>
  );
};