I am using the following code to obtain the users idToken before sending it to the backend as an authorisation header:
const user = firebase.auth().currentUser
const idToken = await user.getIdToken()
sent like this:
var res = await axios.post(backUrl "account/load_balance", {
uid: uid,
id: id
},
{
headers: {
Authorization: 'Bearer ' idToken
}});
It works well but on one of my pages the request is sent to the server before idtoken variable has filled and the user is still null.
I have read that i need to implement onAuthStateChanged as it waits for the token before triggering: https://firebase.google.com/docs/auth/web/manage-users#web-version-8
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
var uid = user.uid;
// ...
} else {
// User is signed out
// ...
}
});
However i am unsure how to implement this in to my code. Can anyone advise?
Full code:
const RoutingForPortfolio = (props) => {
let uid = localStorage.getItem("account-info");
let { id } = useParams();
const loadBlockchainData = async (dispatch) => {
if (id === null || id === undefined) {
id = "test";
}
const user = firebase.auth().currentUser
const idToken = await user.getIdToken()
console.log(idToken)
var res = await axios.post(backUrl "account/load_balance", {
uid: uid,
id: id
},
{
headers: {
Authorization: 'Bearer ' idToken
}});
if (res.data === null) {
await wait(2);
document.location.href = "/logout"
return;
}
else {
// const web3 = new Web3(new Web3.providers.HttpProvider('https://data.stocksfc.com:3200'));
// dispatch(web3Loaded(web3));
const account = res.data.address;
dispatch(web3AccountLoaded(account));
localStorage.setItem("account-address", account);
if (res.data.token_flag && res.data.exchange_flag) {
await dispatch(setLoginUserName(res.data.name));
await dispatch(setLoginUserEmail(res.data.email));
if (res.data.balance !== null) {
await dispatch(etherBalanceLoaded(res.data.balance[0]));
await dispatch(tokenBalanceLoaded(res.data.balance[1]));
await dispatch(exchangeEtherBalanceLoaded(res.data.balance[2]));
await dispatch(exchangeTokenBalanceLoaded(res.data.balance[3]));
}
}
else {
Swal.fire({
icon: "error",
title: "Error...",
text: "Error 485 - Please report to admin",
});
return;
}
}
};
useEffect(() => {
if (uid) {
loadBlockchainData(props.dispatch);
}
}, [props.dispatch, uid]);
return (
<>
{uid ? (
<div>
<Portfolio id={id} />
</div>
) : (
<Login />
)}
</>
);
};
CodePudding user response:
As you correctly identified, firebase.auth().currentUser
is a synchronous action that only gets the user object when it is called. You've also correctly surmised that you instead need to use firebase.auth().onAuthStateChanged()
to wait to check if the user is logged in.
This can be achieved by wrapping an onAuthStateChanged
listener into a Promise
where it is immediately detached after being called once.
function getValidatedUser() {
return new Promise((resolve, reject) => {
const unsubscribe = firebase.auth()
.onAuthStateChanged(
(user) => {
unsubscribe();
resolve(user);
},
reject // pass up any errors attaching the listener
);
});
}
This now allows you to use:
const user = await getValidatedUser();
if (!user) {
// todo: handle no user signed in, such as:
throw new Error("User not signed in!");
}
// if here, user is User object
const idToken = await user.getIdToken()