I currently have a file for authentication called AuthContext.js and a seperate file called Login.js for my login page. Firebase is only imported into the AuthContext file and it currently works, the method appears as follows
function signup(email, password) {
return auth.createUserWithEmailAndPassword(email, password);
}
And the method is called in the Login file inside of an async function.
async function handleSubmit(e) {
e.preventDefault();
if (passwordRef.current.value !== passwordConfirmRef.current.value) {
return setError("Passwords do not match");
}
try {
setError("");
setLoading(true);
await signup(emailRef.current.value, passwordRef.current.value);
navigate("/");
} catch (err) {
setError("Failed to create an account");
}
setLoading(false);
}
I am now attempting to integrate email authentication into the function. I would like to get it to where, if an account remains unverified for a elongated duration then it will delete the account, but I think that Is likely in the Firebase settings.
I found this article on geeksforgeeks that suggested calling the method in the following manner:
const signup = ()=>{
auth.createUserWithEmailAndPassword(email , password)
.then((userCredential)=>{
// send verification mail.
userCredential.user.sendEmailVerification();
auth.signOut();
alert("Email sent");
})
.catch(alert);
}
However, when I attempted to return this function in the form
function signup(email, password) {
return auth.createUserWithEmailAndPassword(email, password).then(
(userCredentials) => {
userCredentials.user.sendEmailVerification();
auth.signOut();
auth.alert("Please verify your email address before logging in.");
}
);
}
It does not work properly, and I get error messages every time I try to sign up a new account. How do I properly implement this to where the promise returns properly and displays the correct message? Is there a way for me to link them to the /login page with an alert already on it that says "Please verify your email address before logging in."? Thanks in advance for any help!
Related
I am implementing a cloud function for updating the current user's password.
Basically, the logic I want to follow is:
(Client side)
0. Complete form and submit the data (current password and new password).
(Backend)
1. Get the current user email from the callable function context.
2. Re-authenticate the current user using the provided current password.
2.1. If success, change the password and send a notification email.
2.2. Else, throw an error.
Here is my current code:
const { auth, functions } = require("../../services/firebase");
...
exports.updatePassword = functions
.region("us-central1")
.runWith({ memory: "1GB", timeoutSeconds: 120 })
.https.onCall(async (data, context) => {
const { currentPassowrd, newPassword } = data;
const { email, uid: userId } = context.auth.token;
if (!userId) {
// throw ...
}
try {
//
// Problem: `firebase-admin` authentication doesn't include
// the `signInWithEmailAndPassword()` method...
//
await auth.signInWithEmailAndPassword(email, currentPassowrd);
await auth.updateUser(userId, {
password: newPassword,
});
sendPasswordUpdateEmail(email);
} catch (err) {
// ...
throw AuthErrors.cannotUpdatePassword();
}
});
My problem is that the firebase-admin package doesn't include the signInWithEmailAndPassword, and I need a way to handle this, to check that "currentPassword" is correct, inside my function.
My other option, if the one I have described is not possible, is to update the password using the firebase sdk in the client side, and then to call a firebase function to send the notification email.
Strictly speaking you don't need to re-authenticate the user in the Cloud Function: If you get a value for context.auth.uid in your Callable Cloud Function, it means that the user is authenticated in the front-end and you can therefore safely call the updateUser() method.
If you want to deal with the case when the user left his device opened, and someone updates his password, as explained in the comments under your question, I would suggest you use the reauthenticateWithCredential() method in the front-end, which re-authenticates a user using a fresh credential.
Do as follows:
import {
EmailAuthProvider,
getAuth,
reauthenticateWithCredential,
} from 'firebase/auth'
const email = auth.currentUser.email;
// Capture the password value
// e.g. via a pop-up window
const password = ...;
const auth = getAuth();
const credential = EmailAuthProvider.credential(
email,
password
);
await reauthenticateWithCredential(
auth.currentUser,
credential
);
// If no error is thrown, you can call the Callable Cloud Function, knowing the user has just re-signed-in.
I am working for a task related to react native app based user management. After they successful sign up themselves as a user, I include a data which is store Boolean value and I have named it as emailVerifiedAccount (this is the stored data if they registered themselves successful and indicate if they are a verified user if they click on email verification link send to them). In login screen I would like to check the Boolean value that I get from firestore in onAuthStateChanged and only direct them to index screen when the emailVerifiedAccount that I get is return true. Code below is authstatechanged for login screen .
const onAuthStateChanged = (user) => {
if (user) {
checkEmailVerifiedAccount=firestoreService.isEmailVerifiedAccount(user);
try {
if(checkEmailVerifiedAccount===true){
navigation.navigate('Index');
}else{
Alert.alert(
"Verify as a Playbookx user",
"Please click on email verification link send to your email. If you do not receive any email ,please contact user support",
"You may come back to login again after you verified your email",
[
{ text: "OK"}
]
)
}
} catch (error) {
console.log(error);
}
}
};
and in firestore.js is where all the code and function that is related with Firestore database. isEmailVerifiedAccount is one of the class object which checking verified user using email.
isEmailVerifiedAccount = async (user) => {
return await firestore()
.collection('users')
.doc(user)
.get()
.then(snapshot => {
if (snapshot.exists){
const user=snapshot.data();
const emailVerifiedAccount={
emailVerifiedAccount:user.emailVerifiedAccount
};
return emailVerifiedAccount
}
})
.catch(error => {
console.log(error);
});
};
And the problem that I facing right now is that, it lead me to index screen when the emailVerifiedAccount in Firestore is false. Picture below is the structure for Firestore.
Your isEmailVerifiedAccount function is asynchronous, so when calling it you have to await the result:
checkEmailVerifiedAccount = await firestoreService.isEmailVerifiedAccount(user);
const checkEmailVerifiedAccount = firestoreService.isEmailVerifiedAccount(user);
checkEmailVerifiedAcccount seems to be an object but you are checking if it's equal to true. Either return user.emailVerifiedAccount directly from isEmailVerifiedAccount function or read that property like this:
const checkEmailVerifiedAccount = firestoreService.isEmailVerifiedAccount(user);
if(checkEmailVerifiedAccount.emailVerifiedAccount) {
navigation.navigate('Index');
}
I am a newbie to firebase. I need a way to verify an email address during the signup process. The user should NOT be automatically logged in without verification. What code can I add to allow me to check if the email is verified or not. If yes, the user can login. If not, they need to very first. Please see code below
async registerUser(customer: Customer, password: string) {
try {
const newUserCredential = await this.firebaseAuth.createUser(
customer.email,
password
);
// store user details in firestore
this.firebaseStore.storeUserDetails(newUserCredential, customer);
// send email verification
await newUserCredential.user.sendEmailVerification();
} catch (error) {
console.error(error);
throw new Error(error.message);
}
}
You just need to check the emailVerified property on the User object.
const {emailVerified} = this.firebaseAuth.currentUser
if (emailVerified) {
// Email is verified
} else {
console.log("Email is not verified")
// Alert user
}
I don't understand, why firebase.auth().sendSignInLinkToEmail(email, settings) sends a login email to an email address which has not been registered yet.
Moreover, when I then click the link, which opens the site where I check the email with firebase.auth().signInWithEmailLink(email, signInEmailLink), the email gets registered and the user is logged in!
I think that's curios, because I've build a separate registration process.
I would expect, that I get an error when I call firebase.auth().sendSignInLinkToEmail(email, settings) with an unregistered email.
What am I doing wrong?
That's my code for sending the email and signing in when user clicked the link.
async login(email) {
try {
const settings = {
handleCodeInApp: true,
url: encodeURI(`${location.protocol}//${location.host}/#!/user/verifizieren`),
};
await firebase.auth().sendSignInLinkToEmail(email, settings);
window.localStorage.setItem(storageKeyEmail, email);
} catch(error) {
console.error(error);
throw error;
}
},
async verify(email, link) {
const signInEmailLink = link || window.location.href;
if(!firebase.auth().isSignInWithEmailLink(signInEmailLink)) {
return Promise.reject('auth/link-invalid');
}
try {
await firebase.auth().signInWithEmailLink(email, signInEmailLink);
firebase.auth().currentUser.reload();
firebase.auth().currentUser.getIdToken(true);
window.localStorage.removeItem(storageKeyEmail);
} catch(error) {
console.error(error);
throw error;
}
},
Sign in with email link is designed to also work for new email accounts. The accounts are not required to already exist.
If you want to block the email sending, you can check if the account exists before hand:
firebase.auth().fetchSignInMethodsForEmail(email).then((signInMethods) => {
if (signInMethods.length === 0) {
// New user.
} else {
// Existing user.
}
});
However, the above is only client side enforced and thus can be bypassed by the user by calling the REST API.
I am trying to change/update a user's email address using :
firebase.auth().changeEmail({oldEmail, newEmail, password}, cb)
But I am getting ...changeEmail is not a function error. I found the reference here from the old firebase docu.
So how to I do it in the 3.x version? Because I cant find a reference in the new documentation.
You're looking for the updateEmail() method on the firebase.User object: https://firebase.google.com/docs/reference/js/firebase.User#updateEmail
Since this is on the user object, your user will already have to be signed in. Hence it only requires the password.
Simple usage:
firebase.auth()
.signInWithEmailAndPassword('you#domain.example', 'correcthorsebatterystaple')
.then(function(userCredential) {
userCredential.user.updateEmail('newyou#domain.example')
})
If someone is looking for updating a user's email via Firebase Admin, it's documented over here and can be performed with:
admin.auth().updateUser(uid, {
email: "modifiedUser#example.com"
});
FOR FIREBASE V9 (modular) USERS:
The accepted answer will not apply to you. Instead, you can do this, i.e., import { updateEmail } and use it like any other import. The following code was copy/pasted directly from the fb docs at https://firebase.google.com/docs/auth/web/manage-users
Happy coding!
import { getAuth, updateEmail } from "firebase/auth";
const auth = getAuth();
updateEmail(auth.currentUser, "user#example.com").then(() => {
// Email updated!
// ...
}).catch((error) => {
// An error occurred
// ...
});
You can do this directly with AngularFire2, you just need to add "currentUser" to your path.
this.af.auth.currentUser.updateEmail(email)
.then(() => {
...
});
You will also need to reauthenticate the login prior to calling this as Firebase requires a fresh authentication to perform certain account functions such as deleting the account, changing the email or the password.
For the project I just implemented this on, I just included the login as part of the change password/email forms and then called "signInWithEmailAndPassword" just prior to the "updateEmail" call.
To update the password just do the following:
this.af.auth.currentUser.updatePassword(password)
.then(() => {
...
});
updateEmail needs to happen right after sign in due to email being a security sensitive info
Example for Kotlin
// need to sign user in immediately before updating the email
auth.signInWithEmailAndPassword("currentEmail","currentPassword")
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success now update email
auth.currentUser!!.updateEmail(newEmail)
.addOnCompleteListener{ task ->
if (task.isSuccessful) {
// email update completed
}else{
// email update failed
}
}
} else {
// sign in failed
}
}
async updateEmail() {
const auth = firebase.auth();
try {
const usercred = await auth.currentUser.updateEmail(this.email.value);
console.log('Email updated!!')
} catch(err) {
console.log(err)
}
}
You can use this to update email with Firebase.
Firebase v9:
const changeEmail = (userInput) => {
const { newEmail, pass } = userInput
signInWithEmailAndPassword(auth, oldEmail, pass)
.then(cred => updateEmail(cred.user, newEmail))
}