K20GH
K20GH

Reputation: 6281

Monitor Firebase Auth Object property changes?

I've implemented by own email verification process using Firebase Auth and Firestore as I needed to use custom email templates. Client side, the UI is done with React.

The process is as follows:

1) User registers and I call firebase.auth().createUserWithEmailAndPassword()

2) My Cloud Function listens for when a new user is created using functions.auth.user().onCreate(). It generates a record in Firestore using their UID as the doc ID, and sets a token and creation time.

3) The user clicks the activation link in their email, loads a UI view which calls a cloud function that validates the token. Upon validation, it updates the auth object for the user:

admin.auth().updateUser(uid, {
   emailVerified: true
});

4) In my UI where I call the function, I wait for the response and then send them to a page where they can select their plan

    async function actionCode() {
        try {
            await firebase.functions().httpsCallable('verifyToken')({
                token: match.params.token
            });
            history.push('/select-plan');
        } catch (e) {
            setAlert({
                message: "Error",
                description: e.message,
                type: "error"
            });
            setLoading(false);
        }
    }

The problem I am having though is within my App, I do routing based on the firebase.auth().currentUser object. For example (some code removed..)

function App() {
    const [loggedIn, setLoggedIn] = useState(false);
    const [user, setUser] = useState(null);

    useEffect(() => {
        if(firebase.auth().currentUser) setLoggedIn(true);

        return firebase.auth().onAuthStateChanged(function(user) {
            if (user) {
                setUser(user);
                setLoggedIn(true);
                setLoading(false);
            } else {
                setLoggedIn(false);
                setUser(null);
                setLoading(false);
            }
        });
    });

    if(loading) return <div></div>

    if(!loggedIn) {
        return authWrapper(
            <Switch>
                <Route path="/login" component={Login} />
                <Route path="/register" component={Register} />
                <Redirect to="/login" />
            </Switch>
        );
    }

    if(!user.emailVerified) {
        return authWrapper(
            <Switch>
                <Route path="/register-success" component={RegisterSuccess} />
                <Route path="/activate-account/:token" component={ActivateAccount} />
                <Redirect to="/register-success" />
            </Switch>
        );
    }

    return authWrapper(
        <div>
            Logged In etc
        </div>
    );
}

The problem I have is that when you change the auth object, it is not reflected in the currentUser object, nor does the onAuthStateChanged function have the updated properties. This only happens if you reload the entire app.

Does anyone know of a way I can progress here. Since the currentUser object doesn't update, my routing breaks and the user cannot progress any further until they reload the app.

Upvotes: 4

Views: 746

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 599631

Setting the email address to verified is a pure server-side operation. It does not automatically notify the client that the user profile has been updated. So the client will only pick up the changes once it refreshes its token, either when you restart the application, or when the token is auto-refreshed (which happens every hour).

If you want to pick up the updated properties sooner, you can force a refresh of the token by calling User.reload(). There are a few common ways to do this:

  1. Call it periodically in your app while you're waiting for the email address to be verified.
  2. Have a button in your app that the user clicks to refresh the token, e.g. I verified my email address.
  3. When the app comes back to the foreground, as typically the user will cause the email address to be verified by an action in another app.
  4. By sending a notification from your server to the app when the email address is verified. You could use an FCM data message for this, or any other client-to-server push mechanism.

Upvotes: 5

Related Questions