Reputation: 6281
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
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:
Upvotes: 5