How to validate firebase user current password - javascript

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

Related

how to get the user email from the applyActionCode in Firebase?

I have my own custom server that also records users. So what I want to achieve is, after a user registers, Then I will generate a custom token by signInWithCustomToken, then send a sendEmailVerification to them for verifying.
Here is how I generate the custom token from my backend
FirebaseAuth.getInstance().createCustomToken(id, additionalClaims)
Then in my front end, it will signInWithCustomToken right after they registered. (this is to get the user for sendEmailVerification)
await axios
.post(`/authorization/sign-up`, {
username: inputData.username,
password: inputData.password,
})
.then(async (response) => {
const auth = getAuth();
signInWithCustomToken(auth, response.data.content.token)
.then(async(userCredential) => {
await updateEmail(userCredential.user, inputData.username)
const actionCodeSettings = {
url: `https://domain host/test?email=${inputData.username}`
};
await sendEmailVerification(userCredential.user, actionCodeSettings)
navigate("/"); //login page
})
})
.catch((error) => toast(error.message));
} else {
toast("Please input valid data");
}
In my Login Backend I will check my database's user, whether their email has been verified or not. So if its not verified, the login will be rejected
select * from user where user = foo and password = foo and isEmailVerified = true
In my front-end verify-email page :
console.log("verifying email")
const mode = searchParams.get('mode') // mode = verifyEmail
const oobCode = searchParams.get('oobCode')
const auth = getAuth()
applyActionCode(auth, oobCode).then(() => {
//how to update my backend based on the user's email
})
Then in my Backend I will check the decodedToken.isEmailVerified, if its false, I will force them to activate their email first.
So, after the user click the activation link, it will be redirected to my verify-email page, which then will also update the status of the user in my database. But, I notice that the applyActionCode function returns Promise<void>.
For example if I register my email through my PC/Laptop, then I confirm the verify email through my phone / table / other device, then I won't be able to get the currentUser to retrieve some data, ex. their email.
I also cannot put the email into the continueUrl parameter right? since the user might change the parameter themself.
So How can I get the user's email safely after applyActionCode?
Or do I just redirect them back to the login page, then each time after they click login, I will compare the decodedToken.isEmailVerified vs my database's isEmailVerified ?

Firebase signInWithEmailAndPassword and onAuthStateChanged Realtime Database logging issue

I'm working on a webpage using HTML5 CCS etc, where it uses a authentication process using firebase for users. Its my first time ever working on firebase, so i still have no idea how to correctly code using it.
I manually add a admin user on firebase, so i can use those credentials to log in to the webpage. In the signInWithEmailAndPassword i used a code to log into the console some information about credentials, but whats happening is that while it does work (the authentication). The only way it logs info into the console is when i don't redirect the user to another page using the onAuthStateChanged (basically not using it at all).
Basically it authenticates correctly, but its doesn't log the info in the realtime database unless i remove the onAuthStateChanged.
Here is the code
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
const dt = new Date();
update(ref(database, 'users/' + user.uid), {
Email: email,
Password: password,
Last_Login: dt
})
alert('Usuario ingresado!')
location.href = 'test.html'
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
alert(errorMessage)
});
});
const user = auth.currentUser;
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
location.href = 'test.html'
// ...
} else {
// User is signed out
// ...
}
});
I heard this process is asynchronous.
Calls to Firebase (and most modern cloud APIs) are asynchronous, since they may take some time to complete. But as soon as the user is signed in, the local onAuthStateChanged will be called - which interrupts the write to the database.
If the user always actively signs in to this page (so you always call signIn...), then you don't need the onAuthStateChanged handler and can just include the navigation code in the then:
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
const dt = new Date();
update(ref(database, 'users/' + user.uid), {
Email: email,
Password: password,
Last_Login: dt
}).then(() => {
location.href = 'test.html'; // 👈
})
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
alert(errorMessage)
});
});

Send verification email before logging in

This is the code that i'm practicing in to create a new user. I can receive the email verification and confirm it however, the site will still logged me in even if I have not yet confirmed my email yet.
try{
const { user } = await auth.createUserWithEmailAndPassword(email,password);
await user.sendEmailVerification();
await handleUserProfile(user, { displayName});
this.setState({
...initialSate
});
}catch(err){
console.log(err);
}
}
This is the handleUserProfile in another js file.
export const handleUserProfile = async (userAuth, additionalData) => {
if (!userAuth) return;
const {uid} = userAuth;
const userRef = firestore.doc(`users/${uid}`);
//create new user
const snapshot = await userRef.get();
if (!snapshot.exists){
const { displayName, email} = userAuth;
const timestamp = new Date();
//if the user exist does not exist
try{
await userRef.set({
displayName,
email,
createdDate: timestamp,
...additionalData
});
}catch(err){
console.log(err);
}
}
return userRef;
};
Everything is explained in the firebase documentation.
There you have the corresponding code snippets to try.
You would need to narrow down your question with some of this trials.
Even you have the chance to check if user opens the link from a differenc device from which waas signed up.
I think this is the snippet you might need:
// Confirm the link is a sign-in with email link.
if (firebase.auth().isSignInWithEmailLink(window.location.href)) {
// Additional state parameters can also be passed via URL.
// This can be used to continue the user's intended action before triggering
// the sign-in operation.
// Get the email if available. This should be available if the user completes
// the flow on the same device where they started it.
var email = window.localStorage.getItem('emailForSignIn');
if (!email) {
// User opened the link on a different device. To prevent session fixation
// attacks, ask the user to provide the associated email again. For example:
email = window.prompt('Please provide your email for confirmation');
}
// The client SDK will parse the code from the link for you.
firebase.auth().signInWithEmailLink(email, window.location.href)
.then((result) => {
// Clear email from storage.
window.localStorage.removeItem('emailForSignIn');
// You can access the new user via result.user
// Additional user info profile not available via:
// result.additionalUserInfo.profile == null
// You can check if the user is new or existing:
// result.additionalUserInfo.isNewUser
})
.catch((error) => {
// Some error occurred, you can inspect the code: error.code
// Common errors could be invalid email and invalid or expired OTPs.
});
}
The site will still logged me in even if I have not yet confirmed my
email yet.
Yes this is how it is implemented in Firebase: there is nothing, out of the box, that prevents a user with a non-verified email to authenticate to your app.
You should manage that yourself, by:
Checking the email is verified in the back-end security rules (Firestore, Cloud Storage, etc..). For example with a function like:
function isVerifiedEmailUser() {
return request.auth.token.email_verified == true;
}
Possibly redirect and logout the user from your app if his/her email is not verified. For example, right after signing-up, as follows:
try {
const { user } = await auth.createUserWithEmailAndPassword(email,password);
await user.sendEmailVerification();
if (user.emailVerified) {
// display the content, redirect to another page, etc...
} else {
auth.signOut(); // Maybe call that after showing an error message
}
} catch(err){
console.log(err);
}
}
plus, potentially, something similar with signInWithEmailAndPassword() and onAuthStateChanged().

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.
}
});

Firebase authentication and store user in MySQL

I want to know how I can register a user using Firebase authentication, and then proceed to store the user in a MySQL database as well for later use.
I am currently authenticating the user using Firebase, but am not sure how to go about calling a Nodejs API to query the MySQL database.
All the examples I have seen to store users in MySQL are calling the API from the form action itself. However, I want to first authenticate the user with Firebase.
If someone has experience with this I would appreciate the help.
const signupForm = document.querySelector('#sign-up-form');
signupForm.addEventListener('submit', (e) => {
e.preventDefault();
//get user info
const username = signupForm['signup-username'].value;
const email = signupForm['signup-email'].value;
const password = signupForm['signup-password'].value;
//signup the user
auth.createUserWithEmailAndPassword(email, password).then(cred => {
//createUser function returns the user credentials
user = auth.currentUser;
}).then(function () {
user.updateProfile({
displayName: username
})
}).catch((error) => {
//Handle errors
var errorCode = error.code;
var errorMessage = error.message;
// [START_EXCLUDE]
if (errorCode == 'auth/weak-password') {
alert('The password is too weak.');
} else {
alert(errorMessage);
}
});
})
You need to have some sort of backend to send the user and then use Express or some other server make the query and save to MySQL. You can't do it from the client because it's insecure.
eg:
auth.createUserWithEmailAndPassword(email, password).then(async (user) {
user.updateProfile({
displayName: username
})
const result = await axios.post('/api/saveUser/', user, config)
})

Categories

Resources