Correct way to pass async eerror - javascript

I have a function which uses Firebase auth to update a user's email:
export const updateEmail = async (email) => {
const user = auth.currentUser;
return user.updateEmail(email);
};
It is used in a function which gets an email from a form (in React) and tries to update the email. If there is an error, we change the state to reflect that.
handleSave = (e) => {
const email = e.target.email.value;
updateEmail(email).catch((err) => {
this.setState({ didError: true, emailError: err.message });
});
};
However, when an error occurs, in the console I get:
My question is: why does this still say 'Uncaught'? Does the .catch() in handleSave not take care of that?
update
Link to relevant Firebase docs

Assuming updateEmail returns a prmise, I guess you can try:
export const updateEmail = (email) => { // no need for async here
const user = auth.currentUser;
return user.updateEmail(email);
};
handleSave = async (e) => {
const email = e.target.email.value;
try{
await updateEmail(email);
}catch(err){
this.setState({ didError: true, emailError: err.message });
}
};

I'm not quite sure since I don't know so much about Firebase, let me suggest something.
export const updateEmail = async (email) => {
const user = auth.currentUser;
const response = await user.updateEmail(email);
if ( response.error ) {
throw new Error( response.error );
}
return "something else";
};

Related

Firebase: Error (auth/user-token-expired)

let me first note that I am new to firebase and authentication matters ... for several days I am facing a problem with updating email and password functionality using firebase... I want to know if this problem have something with using React and its way of working like re-rendering components ..etc. or is it the use of promises the way i've done in the code below.. I really don't know.
This is the error that I am facing.
Uncaught (in promise) FirebaseError: Firebase: Error (auth/user-token-expired).
Here is the code involved in the situation
firebase.js
const updateUserEmail = (newEmail) => {
return updateEmail(auth.currentUser, newEmail);
}
const updateUserPassword = (newPassword) => {
return updatePassword(auth.currentUser, newPassword);
}
update_profile.jsx
async function handleUpdateProfile() {
setErrorMessage('');
setIsLoading(true);
// check for password equality
if (passwordInputRef.current.value !== confirmPasswordInputRef.current.value) {
setErrorMessage('Passwords do not match');
return;
}
const promises = [];
if (emailInputRef.current.value !== currentUser?.email) {
promises.push(updateUserEmail(emailInputRef.current.value));
}
if (passwordInputRef) {
promises.push(updateUserPassword(passwordInputRef.current.value);
}
Promise.all(promises).then(() => {
navigate('/');
}).catch((err) => {
console.log(currentUser);
console.log(err)
setErrorMessage('Couldn\'t Update Your Profile');
}).finally(() => {
setIsLoading(false);
})
}
I tried auth.currentUser.reload()
like that
auth.currentUser.reload().then(() => {
updateEmail(auth.currentUser, newEmail);
})
I tried Re-authenticate the user using the doc guide here
firebaseDocs
but nothing solved the error.
Finally I have come to an answer for my question that has worked
I am not quite sure, but I believe that the root of my problem returns to two things
Reauthentication of a user
Async matters
Here is how I solved the error
firebase.js
const updateUserEmail = async (newEmail) => {
const email = prompt('Please Enter Your Email');
const password = prompt('Please enter your password');
let credential = EmailAuthProvider.credential(email, password);
await reauthenticateWithCredential(auth.currentUser, credential).then(() => {
updateEmail(auth.currentUser, newEmail);
})
}
const updateUserPassword = async (newPassword) => {
const email = prompt('Please Enter Your Email');
const password = prompt('Please enter your password');
let credential = EmailAuthProvider.credential(email, password);
await reauthenticateWithCredential(auth.currentUser, credential).then(() => {
updatePassword(auth.currentUser, newPassword);
})
}
for each update I prompted the user to give me his email and pass, and then used it for reauthenticating matters.
to fix the async issue.. i made the function to wait for the first update (email) before proceeding to the next update (password) and the next lines of code clarify it more.
update_profile.jsx
let newEmail = false;
let newPassword = false;
if (emailInputRef.current.value !== currentUser?.email) {
newEmail = true;
}
if (passwordInputRef) {
newPassword = true;
}
if (newEmail) {
await updateUserEmail(emailInputRef.current.value);
if (newPassword) {
await updateUserPassword(passwordInputRef.current.value);
}
}
I bet there are better solutions than mine and I'll be happy to read it, but for now this will do the trick.

How to reauthorize a user in Firebase?

Environment
Firebase JavaScript SDK v8
Question
How can I re-authorize an already-logged-in user with their password? What I want to do is like below:
const password = "some-password"
firebase.reAuthorizeUser(password)
.then(() => console.log("ok"))
.catch(() => console.log("ng"))
Thank you in advance.
You are looking for reauthenticateWithCredential method.
const user = firebase.auth().currentUser;
// TODO(you): prompt the user to re-provide their sign-in credentials
const credential = promptForCredentials();
user.reauthenticateWithCredential(credential).then(() => {
// User re-authenticated.
}).catch((error) => {
// An error ocurred
// ...
});
Checkout reauthenticate a user in the documentation.
You can do a switch case based on providerId, below should help.
user.providerData.forEach((profile) => {
switch (profile.providerId) {
case 'google.com':
user
.reauthenticateWithPopup(new firebase.auth.GoogleAuthProvider())
.then((UserCredential) => {
**//reauthenticated successfully!**
})
})
.catch((error) => {
**//Something is wrong**
})
break
case 'password':
// eslint-disable-next-line no-case-declarations
const credentials = firebase.auth.EmailAuthProvider.credential(
user.email,
userPassword
)
user
.reauthenticateWithCredential(credentials)
.then(() => {
**//reauthenticated successfully!**
},
})
})
.catch(() => {
**//Something is wrong**
})
break
})
SDK v9
import {getAuth, reauthenticateWithCredential, EmailAuthProvider} from "firebase/auth";
const reauthenticate = async () => {
try {
const auth = getAuth();
const user = auth.currentUser;
const credential = await EmailAuthProvider.credential(
email,
password
);
await reauthenticateWithCredential(user, credential);
return true
} catch (e) {
return null
}
}
const credential = promptForCredentials();
this is giving me error
cant find variable: promptForCredentials.

Firestore database won't appear

I'm new with Firebase and I'm still learning how it works. I've created a login, signup and logout sections. The users appear registrated and are saved in the authentication section, but I also want to have their data in database when they register for the first time. I thought of using Firestore Database. The problem is that everything seems to work, but nothing appears in my database section. At first I thought that I was not passing any user auth to the function, so i created a condition to test if there's no user auth, then show a warning. However, there's no warning so it means that I passed it properly.
This is how the sign up function works:
export const SignUp = () => {
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const displayName = useRef();
const email = useRef();
const password = useRef();
const passwordConfirmRef = useRef();
const handleSubmit = async e => {
e.preventDefault();
if (password.current.value !== passwordConfirmRef.current.value) {
return setError("Passwords do not match");
}
try {
setError("")
setLoading(true)
const { user } = await auth.createUserWithEmailAndPassword(email.current.value, password.current.value)
const userRef = await handleUserProfile(user, displayName.current.value)
console.log(userRef)
} catch {
setError("Failed to create an account")
}
setLoading(false)
}
I create the user with auth.createUserWithEmailAndPassword and then I pass the user to handleUserProfile
Here's handleUserProfile function:
export const handleUserProfile = async (userAuth, additionalData) => {
if (!userAuth) {
console.warn("No userAuth")
return
}
const { displayName, email } = userAuth;
const timestamp = new Date()
try {
return await firestore.collection("users").add({
displayName,
email,
timestamp,
...additionalData
})
} catch (err) {
console.log(err)
}
return null;
};
Then, nothing appears in my database and nothing gets added. I'm not sure what I'm doing wrong.
The firestore.collections().add function is adding objects (key: value pairs).
It looks like you're not passing an object into handleUserProfile:
const userRef = await handleUserProfile(user, displayName.current.value)
Passing an object into additionalData should solve your issue.

How can I make a callback on Async funcion?

I have the following code which check the user_id if available and then log me in but it logs me in only if I refresh the app. Any idea how to make this happen without this?
This is the order of functions:
First when you click the login button from Login.js:
<TouchableOpacity onPress={handleSubmit(_signIn)} style={{margin: 10, alignItems: 'center'}}>
then _signIn function which is in Login.js
_signIn = (values, dispatch) => {
const email = values.email;
const password = values.password;
dispatch(loginUser(email, password));
}
Now we dispatched email and password to loginUser from authActions.js
export function loginUser(email, password) {
return function (dispatch) {
return axios.post(SIGNIN_URL, { email, password }).then((response) => {
var { user_id, token } = response.data;
onSignIn(user_id); // Here I pass 'user_id' to onSignIn function
}).catch((error) => {
dispatch(addAlert("Could not log in."));
});
};
}
Now we get the user_id from loginUser inside Auth.js
import { AsyncStorage } from "react-native";
const USER_KEY = "auth_key";
export const onSignIn = (user_id) => AsyncStorage.setItem(USER_KEY, user_id);
export const onSignOut = () => AsyncStorage.removeItem(USER_KEY);
export const isSignedIn = () => {
return new Promise((resolve, reject) => {
AsyncStorage.getItem(USER_KEY)
.then(res => {
if (res !== null) {
resolve(true);
} else {
resolve(false);
}
})
.catch(err => reject(err));
});
};
Now in App.js I am calling the function isSignedIn to check if user_id is available and if so will choose which screen to show
constructor(props) {
super(props);
this.state = {
signedIn: false,
checkedSignIn: false
};
}
componentDidMount() {
isSignedIn()
.then(res => this.setState({ signedIn: res, checkedSignIn: true }))
.catch(err => alert("An error occurred"));
}
render() {
const { checkedSignIn, signedIn } = this.state;
// If we haven't checked AsyncStorage yet, don't render anything (better ways to do this)
if (!checkedSignIn) {
return null;
}
const Layout = createRootNavigator(signedIn);
It`s not async function callback issue - I do know how to use it, coz you are already use it in the isSignedIn function.
You did called onSignIn(userId), but you inform nobody about it. Those function, that calls isSignedIn should somehow know about a user logged in.
Based on this issue and previous one, I guess you should choose an architecture of your app (redux or just functional programming or something else) and keep it in mind.
If you wanna use redux, you should dispatch an action about a user logged in and reflect to state change where you need it.

How do I add a user to my Firebase DB with it's unique id when I first create a user for authentication

This may seem like an obvious question, but i'm having trouble creating a user inside of my Firebase db. I'm very new to firebase and coding for that matter.
How I understand it to be is when a user clicks the sign up button, a user with the newly authenticated uid will be created under 'object/users". However the user is always null. In the google documentation it says that sometimes the user is null because it hasn't finished initializing the user. And to put it inside of onAuthStateChanged. This works, however, if I did this a user would be created/overwritten every time they log in or log out.
I suppose what I'm looking for is some solution to create a user inside of my firebase db when a user first signs up with valid information.
Here is my code:
function LoginCtrl($scope) {
$scope.txtEmail = document.getElementById('txtEmail');
$scope.txtPassword = document.getElementById('txtPassword');
$scope.btnLogin = document.getElementById('btnLogin');
$scope.btnSignUp = document.getElementById('btnSignUp');
$scope.btnLogout = document.getElementById('btnLogout');
btnLogin.addEventListener('click', e => {
const email = txtEmail.value;
const pass = txtPassword.value;
const auth = firebase.auth();
const promise = auth.signInWithEmailAndPassword(email, pass);
promise.catch(e => console.log(e.message));
});
btnSignUp.addEventListener('click', e => {
const email = txtEmail.value;
const pass = txtPassword.value;
const auth = firebase.auth();
const promise = auth.createUserWithEmailAndPassword(email, pass);
promise.catch(e => console.log(e.message));
createUser();
});
function createUser() {
var user = firebase.auth().currentUser;
if (user != null) {
const dbRefObject = firebase.database().ref().child('object');
const dbRefList = dbRefObject.child("users/" + user.uid);
dbRefList.set({
name: "jim",
});
}
}
btnLogout.addEventListener('click', e => {
firebase.auth().signOut();
});
firebase.auth().onAuthStateChanged(firebaseUser => {
if (firebaseUser) {
console.log(firebaseUser);
btnLogout.classList.remove('hide');
btnLogin.classList.add('hide');
} else {
console.log('not logged in');
btnLogout.classList.add('hide');
btnLogin.classList.remove('hide');
}
})
}
The culprit is in these three lines:
const promise = auth.createUserWithEmailAndPassword(email, pass);
promise.catch(e => console.log(e.message));
createUser();
You are firing off createUser() too soon. In the above code fragment, this happens right after starting the (asynchronous) registration process – the SDK never had a chance to actually create the user in the auth database!
The createUserWithEmailAndPassword returns a promise, as reflected in your local variable. What you need to do here is chaining the two calls, i.e. start the database manipulation after the promise is fulfilled.
Try this:
// inside the click handler
btnSignUp.addEventListener('click', e => {
const email = txtEmail.value;
const pass = txtPassword.value;
const auth = firebase.auth();
auth.createUserWithEmailAndPassword(email, pass)
.then(user => createUser(user)) // <-- important!
.catch(e => console.log(e.message));
});
function createUser(user) {
// no need for currentUser if you pass it in as an argument
if (user) {
...
}
}

Categories