Firebase Email Verification is not working - javascript

In my code user is created but Email Verification is not working.
Here is my code:
const signup = () => {
auth.createUserWithEmailAndPassword(user.email, user.password)
.then((userCredential) => {
// send verification mail.
userCredential.user.sendEmailVerification();
//auth.signOut();
alert("Email sent");
})
.catch(alert);
}

You should wait for the sendEmailVerification promise to complete before showing the "Email sent" alert as it blocks the thread and prevents the sendEmailVerification from doing what it needs to.
const signup = () => {
auth.createUserWithEmailAndPassword(user.email,user.password)
.then((userCredential) => {
// send verification mail.
return userCredential.user.sendEmailVerification();
})
.then(() => alert("Email sent"))
.catch((err) => alert(err));
}
Note: Take care in where you call signup as the browser may be reloading the page before any of the above steps complete because you aren't chaining the Promises. Either return false to the caller (to prevent the form element submitting) or the Promise chain to the caller (to handle success/failure of the actions).

Related

Password is gone when logging in a firebase account that was created using Email and Password

I created an app that supports both Email/Password and Google authentication. I found that if I created an account in a first way, but logged out and in again with Google, the origin password was gone, and no way to sign in with email anymore. Is there any way to avoid so?
// Google authentication
const signInWithGoogle = useCallback(
async event => {
event.preventDefault();
const provider = new firebase.auth.GoogleAuthProvider();
try {
await firebaseApp
.auth()
.signInWithRedirect(provider)
.then(function(result) {
var user = result.user.providerId;
alert(user);
});
history.push("/transfer");
} catch(error) {
alert(error.message);
}
},
[history]
);
//Email/Password sign-in
const handleLogin = useCallback(
async event => {
event.preventDefault();
const { email, password } = event.target.elements;
try {
await firebaseApp
.auth()
.signInWithEmailAndPassword(email.value, password.value)
.then(function(result) {
var user = result.user.providerId;
alert(user);
});
history.push("/transfer");
} catch (error) {
alert(error);
}
},
[history]
);
// Email/Password sign-up
const handleSignUp = useCallback(async event => {
event.preventDefault();
const { email, password } = event.target.elements;
try {
await firebaseApp
.auth()
.createUserWithEmailAndPassword(email.value, password.value);
history.push("/usersignupcred");
} catch (error) {
alert(error);
}
}, [history]);
Here in the documentation you can see this explanation:
Note that some providers, such as Google and Microsoft, serve as both email and social identity providers. Email providers are considered authoritative for all addresses related to their hosted email domain. This means a user logging in with an email address hosted by the same provider will never raise this error (for example, signing in with Google using an #gmail.com email, or Microsoft using an #live.com or #outlook.com email).
I would recommend to use as similar approach like here from the docu:
// User tries to sign in with Facebook.
auth.signInWithPopup(new firebase.auth.FacebookAuthProvider()).catch(err => {
// User's email already exists.
if (err.code === 'auth/account-exists-with-different-credential') {
// The pending Facebook credential.
var pendingCred = err.credential;
// The provider account's email address.
var email = err.email;
// Get the sign-in methods for this email.
auth.fetchSignInMethodsForEmail(email).then(methods => {
// If the user has several sign-in methods, the first method
// in the list will be the "recommended" method to use.
if (methods[0] === 'password') {
// TODO: Ask the user for their password.
// In real scenario, you should handle this asynchronously.
var password = promptUserForPassword();
auth.signInWithEmailAndPassword(email, password).then(result => {
return result.user.linkWithCredential(pendingCred);
}).then(() => {
// Facebook account successfully linked to the existing user.
goToApp();
});
return;
}
// All other cases are external providers.
// Construct provider object for that provider.
// TODO: Implement getProviderForProviderId.
var provider = getProviderForProviderId(methods[0]);
// At this point, you should let the user know that they already have an
// account with a different provider, and validate they want to sign in
// with the new provider.
// Note: Browsers usually block popups triggered asynchronously, so in
// real app, you should ask the user to click on a "Continue" button
// that will trigger signInWithPopup().
auth.signInWithPopup(provider).then(result => {
// Note: Identity Platform doesn't control the provider's sign-in
// flow, so it's possible for the user to sign in with an account
// with a different email from the first one.
// Link the Facebook credential. We have access to the pending
// credential, so we can directly call the link method.
result.user.linkWithCredential(pendingCred).then(usercred => {
// Success.
goToApp();
});
});
});
}
});
But instead of waiting for the error to be raised (none will be raised if using Google login as you also explained in your case) try always to call first fetchSignInMethodsForEmail and if the user has the email provider and tries now to use the Google one first log him in with the email provider and link him later with the Google provider.

How to prevent Firebase Cloud Function from crashing and how to send error message as response?

I've created a simple createUser function which is executed on call. I have one problem though. The function is crashing when the user is trying to register with an already existing email. I mean, it's ok, since no one wants to have 2 users with the same email address but I want to prevent crushing function, instead, I want to send an error message as a response.
export const createUserTest = functions.https.onCall((data, context) => {
const {email, password} = data;
return new Promise((resolve, reject)=>{
try{
admin
.auth()
.createUser({
email: email,
emailVerified: false,
password: password,
disabled: false,
})
.then((user) => {
resolve({
result: 'success',
user: user,
}) ;
})
.catch((error) => {
reject(error) ;
});
}catch(error) {
reject (error)
}
})
});
I tried to put the function in to try/catch block but it didn't help. Do you have an idea of how I can achieve my goal?
As explained in the doc for Callable Cloud Functions, "to ensure the client gets useful error details, return errors from a callable by throwing (or returning a Promise rejected with) an instance of functions.https.HttpsError".
The error has a code attribute that can be one of the values listed here. In your case, the most appropriate seems to be already-exists.
On, the other hand, you'll find here the Admin SDK Authentication errors list and you'll see that in case the provided email is already in use by an existing user the error code is auth/email-already-exists.
So you can adapt your code as follows:
export const createUserTest = functions.https.onCall((data, context) => {
const { email, password } = data;
return admin
.auth()
.createUser({
email: email,
emailVerified: false,
password: password,
disabled: false,
})
.then((user) => {
return {
result: 'success',
user: user,
}
})
.catch((error) => {
if (error.code === 'auth/email-already-exists') {
throw new functions.https.HttpsError('already-exists', 'The provided email is already in use by an existing user');
} else {
throw new functions.https.HttpsError('...other code....', '...');
// If an error other than HttpsError is thrown, your client instead receives an error with the message INTERNAL and the code internal.
}
});
});
See here in the doc, how to handle errors on the client side. If error.code == 'already-exists' you know that it's because the email is already in use.

Trying to get the status of firebase user authentication

I have an authentication function using firebase which is just the auth code from the firebase documentation
export const signIn = (email,password) => {
firebase.auth().signInWithEmailAndPassword(email, password).then(()=>{
alert('Sign in Successful!')
}).catch(function(error) {
alert(error.message)
});
}
I call it like this
signIn(mail, password)
When I call it in my code, It works perfectly and the proper alerts appear. However, I want to actually receive something from my authentication function, like True or False if the user successfully logged in or not. Is there a way for me to receive this value from my function or any workarounds?
//evaluates to True if logged in successfully and vice versa
let authState = signIn(this.mail, this.password)
There's a couple approaches you could take with this, the first that comes to mind is the following:
export const signIn = (email, password) => {
return firebase.auth().signInWithEmailAndPassword(email, password).then(userCredential => {
alert('Sign in Successful!');
return true;
}).catch(error => {
alert(error.message);
return false;
});
}
// ......
let authState = await signIn(this.mail, this.password);
In promises you're able to return values from the .then() or .catch() method and then use that resolved value further in your code.
If you want to know when a user is signed in, no matter how they were signed in, you should instead use an auth state observer to set up a callback that will be invoked whenever the user becomes signed in or out, as shown in the documentation:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// User is signed out.
}
});

Email is not verified firebase

I am using Angular 9 and firebase. When a user signs up they get a verification email and the when they click it they are taken to a verification screen saying it has been verified. But they are getting an error when they do to login saying email is not verified. The page has to be reloaded for it to work.
onSignup(form: NgForm) {
this.submitted = true;
if (form.valid) {
this.authService.RegisterUser(this.signup.username, this.signup.password)
.then((res) => {
// Do something here
this.authService.SendVerificationMail();
this.router.navigateByUrl('/email-verification');
}).catch((error) => {
window.alert(error.message)
})
}
}
Here is the send verification mail from the auth service.
SendVerificationMail() {
this.ngFireAuth.onAuthStateChanged(function(user) {
user.sendEmailVerification();
});
}
Why is this happening and what should I do?

How to validate firebase user current password

I am creating a form, in react-redux to change user password. I am wondering how can I validate the user current password in order to change to new one.
in my form I have 2 fields: old password, new password.
this is my action:
const { currentUser } = auth
currentUser.updatePassword(newPassword)
.then(
success => {
dispatch({
type: CHANGE_USER_PASSWORD_SUCCESS,
payload: currentUser
})
},
error => {
dispatch({
type: CHANGE_USER_PASSWORD_FAIL,
error: error.message
})
}
)
I am wondering, how to validate the old password in firebase? Should I use signInWithEmailAndPassword()? Or, is there a function to validate the current password without calling the signIn again, since my user is already logged in?
Thanks
Well, I believe you want the user to enter the old password just to verify whether it's the actual owner of the account or not.
Firebase handles this situation very well, you just need to call the updatePassword method on the user object and pass in the new password.
const changePassword = async newPassword => {
const user = firebase.auth().currentUser;
try {
await user.updatePassword(newPassword)
console.log('Password Updated!')
} catch (err) {
console.log(err)
}
}
If it's been quite a while that the user last logged in then firebase will return back an error -
"This operation is sensitive and requires recent authentication. Log in before retrying this request."
Thus, you don't really need to check the old password as firebase does it for you.
But if you just want to do it in one go, without having the user to log in again.
There's a way for that as well.
There is a method on user object reauthenticateAndRetrieveDataWithCredential you just need to pass in a cred object(email and password) and it refreshes the auth token.
const reauthenticate = currentPassword => {
const user = firebase.auth().currentUser;
const cred = firebase.auth.EmailAuthProvider.credential(
user.email, currentPassword);
return user.reauthenticateAndRetrieveDataWithCredential(cred);
}
In your particular case, you can have something like this
const changePassword = async (oldPassword, newPassword) => {
const user = firebase.auth().currentUser
try {
// reauthenticating
await this.reauthenticate(oldPassword)
// updating password
await user.updatePassword(newPassword)
} catch(err){
console.log(err)
}
}
Learn more about firebase reauth - https://firebase.google.com/docs/auth/web/manage-users#re-authenticate_a_user
Hope it helps

Categories

Resources