29

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?

Aritra Roy
  • 15,355
  • 10
  • 73
  • 107
  • 3
    I think it's a bug. Hopefully, a Firebaser will answer. Related question: http://stackoverflow.com/a/40967256/4815718. – Bob Snyder Dec 07 '16 at 18:06

7 Answers7

16

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)

Woody Jean-louis
  • 475
  • 6
  • 12
  • This worked for me. Do you log out and log in before that? Haven't tried it without yet. – just_deko Aug 25 '17 at 20:18
  • @just_deko I did not log out and log in. – Woody Jean-louis Aug 25 '17 at 23:45
  • 1
    This is still not working for me. I had to sign the user out and back in. –  Nov 14 '17 at 01:43
  • @WalterMorawa are you checking the user object that is coming from Auth.auth().addStateDidChangeListener { (auth, user) in // ... } ? – Woody Jean-louis Nov 14 '17 at 23:57
  • I just tried using reload without auth- could you post some more code in your answer? I'm not sure what you mean by FIRAuth.auth()? –  Nov 16 '17 at 14:44
  • 1
    @WalterMorawa I updated my response. Hopefully it helps. also it would help to read the documentation and guides https://firebase.google.com/docs/auth/ios/manage-users – Woody Jean-louis Nov 17 '17 at 02:09
5

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.

user2887478
  • 51
  • 1
  • 3
4

Just do this:

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

if(user.isEmailVerified())
{
     .....
      ...
}
3

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!

Flame
  • 6,663
  • 3
  • 33
  • 53
1

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'));
      }
    })
  }
BBacon
  • 2,456
  • 5
  • 32
  • 52
0

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>
  );
};
Fanchen Bao
  • 3,310
  • 1
  • 21
  • 34
0

I finally figured it out. Firebase removed the auth.applyActionCode from the auth function and moved Email Verification to it's REST Api. Here is the documentation link. https://firebase.google.com/docs/reference/rest/auth#section-verify-password-reset-code

If you are using NextJS you can use the router query to parse the email verification link that was sent to the user and get the ooBcode and ApiKey from it like this:

const router = useRouter()
const { oobCode, apiKey } = router.query

Then you will need to do a POST request to the REST Api to verify the two elements like this.

const res = await fetch(`https://identitytoolkit.googleapis.com/v1/accounts:update?key=${apiKey}`, {
        method: 'POST',
        headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        },
        body: JSON.stringify({
        oobCode: oobCode
        })
    })

Finally you will get a response object back that will have the users email verified as "true"

if(!res) return;
let data = await res.json()
   console.log(data)
CryptoDevWill
  • 109
  • 1
  • 2