How to update already existing data using token only? - node.js

I have been trying to complete a login/Sign Up API using Node Js and MongoDB, and everything works fine except Forgot Password. I send an email to the user with a link to add new password.
The issue I am having is, How will I extract that specific user when he/she presses the reset button and only update that user's data in the database.

Classic rest password should have this flow:
user select the reset password
user enter its mail
a mail is sent from the system
mail contains a link (with expiration time for security) something like this:
https://acme.com/security/password/reset?code=8df024dfd526
code=8df024dfd526 in the link is related to the mail, so when is clicked, a final UI form will be prompted to the user. You could use this code to identify the email, sending it to your backend
You cannot ask the email again because anyone could change anyone's password.
Some times this code is known as one-time password (OTP).
As its name says: You must ensure that this code works just one time. I mean if user click again, you should show an error.
More details here.
implementation
You just need to persist somewhere (database) the following information:
the generated alphanumeric code with the requested email.
an expiration time for any code
usage count of code

Your email link to the user should be unique and can identify the user.
I do this:
// Generate hashed reset password token
User.resetPasswordToken = (user) => {
const resetToken = crypto.randomBytes(20).toString('hex')
// Update user with hashed token and expire time
user.resetPasswordToken = crypto.createHash('sha256').update(resetToken).digest('hex')
user.resetPasswordExpire = Date.now() + 10 * 60 * 1000
// Return unhashed token for use in email url
return resetToken
}
Here I do two things: 1) update the User in the DB with a resetPasswordToken and an expiration time/date for the resetPassword. Both saved in the database. 2) return an unhashed resetPassword for use in the email link to be sent (see below).
const resetToken = User.resetPasswordToken(user)
await user.save()
const resetUrl = `this link`
// Send email
const message = `<p>Please follow ${resetUrl} in order to create a new password. The link is valid for 10 minutes.</p>`
User now clicks the link and the endpoint extracts the reset token, hashes it and checks if it corresponds with the token saved on this particular user in the database from req.params.resettoken:
const resetPasswordToken = crypto.createHash('sha256').update(req.params.resettoken).digest('hex')
const timeNow = Date.now()
let user = await User.findOne({
where: {
resetPasswordToken,
deletedAt: null,
},
})
Then I perform some checks before I save the new password (encrypted) the user has typed in. I reset the reset token and expiration password in the database to null.
user.password = await encrypt(req.body.password)
user.resetPasswordToken = null
user.resetPasswordExpire = null
try {
await user.save()
} catch (error) {
return next(new ErrorResponse(`The new password could not be saved, please try again later`, 500))
}

Related

Getting field value of a MongoDB document using another value of the same document (NodeJS)

Let's imagine I have an MongoDB document that includes the following data:
{
"username": "test123",
"password": "$2b$10$M2Y3ELsgfvA4KHxdCJkezeHZ1kKgtOA2Jiq4kuwqcRJSovGBu9nLm"
}
The password is hashed using bcrypt. Using MongoDB's official Node driver, I can find the username with:
collection.findOne({ username: req.body.username });
But how can I find the username, then check the password value of the same document (the password related to the username), and finally return the password?
PS. I know the title is very confusing, if you can think of a better way to express it, please comment below.
It's bad practice to send encrypted passwords to the database. It has some security issues. I guess you want to get the user by its username, figure out if it's the right user that you fetched by comparing its password also (password hashes), then do something with that user - for example, return password or so, as you mentioned (I don't see why would you expose password back to anyone).
For encryption, I'm using bcryptjs package, but it's pretty similar. Its function compare() takes the raw password for the first argument, and takes hashed password from the database as the second argument. It hashes the raw password and compares it with the given hash. If hashes match, the password is valid. For a code, you would have something like:
const bcrypt = require('bcryptjs');
// ...
const user = await collection.findOne({ username: req.body.username });
if (!user) throw new Error('User doesn\'t exist');
// first argument "password" is a raw password to compare with the one in the document
const passwordValid = await bcrypt.compare(password, user.password);
if (!passwordValid) throw new Error('Invalid password');
// do whatever you want to do with validated user
// if you want password to return, raw "password" is the one that's in the database also (hash)

Cognito - How to get username from email

When running the following code
const userDetails = {
Username: email,
Pool: userPool
};
const cognitoUser = new amazonCognitoIdentity.CognitoUser(userDetails);
console.log(cognitoUser.getUsername());
the output is the email.
How can I get Cognito user's GUID? (i.e : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
NOTES:
The use-case is general and not related to a specific function like 'signup' or 'login'.
The only parameter the client pass is an email, (i.e no tokens) for example when users forget their password.
As of today you cannot do it.
The UUID/sub is an internal attribute. You can search for users having it with the ListUsers API, but you cannot retrieve it in any other way than through a token.

How to check currentPassword to allow user to change Password

im trying to implement function for "user change password".
i want to do:
1.user choose new password.
2.user needs to enter his current password to varificate.
3.if the user currentPassword correct -> change the password.
the problem: i dont know how to get his current password to check if its correct
in my client side i save his hashPassword ("lfds7fdhas784n23489h42")
so i cant do something like:
if state.user.password===currentPassword because i dont have its state password
i need to somhow get his passwsord from the server and check it, or maybe i need to send the
currentPassword to the back end and check it there ,but i dont know how to implement it...
code:
the Form to update password (in shortcut for better understanding):
<TextInput
placeholder"NewPassword"
onChangeText={setNewPass}/>
<TextInput
placeholder"currentPassword"
onChangeText={setCurrentPass}/>
updateUserPssword(state.userId, token, param, value);
the Function for update the password:
const updateUserPssword = dispatch => async (userId, token, newPass, currentPass) => {
try {
const res = await indexApi.put(
`/user/${userId}`,
{
password: newPass,
},
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
}
For summary, the flow of reset password operation is like:
Step 1: User fills in currentPassword, newPassword and confirmedNewPassword in your React App. What you can validate here is only the similatiry of newPassword and confirmNewPassword.
Step 2: When the newPassword and comfirmedNewPassword are similar you send a request to server with data like this : { current: currentPassword, new: newPassword }
Your main concern is how to do that with axios. Below is an example:
axios.post('/reset-password', {
current: currentPassword,
new: newPassword
})
.then(function (response) {
// success case handle here
})
.catch(function (error) {
// error case handle here
});
When your server gets this request, basically, the following steps should be done
Step 1: Use hash function to hash the currentPassword
Step 2: Compare with hashed password of this user in DB
Step 2.1: If the 2 hashed passwords match, hash the newPassword and save the hashed value to DB
Step 2.2: If the 2 hashed passwords do not match, response error to your React App. (something like: passwords do not match)
You should manipulate all your actions on passwords on the server-side. That's the right flow
User types new password (Don't hash)
User types same password for confirmation (you can compare only this on client-side, if fieldA.value == fieldB.value)
User types old password
You send new password and old password as plain, to the server without hash.
Server hashes your old password with the same SALT and compares if generated hash is equal to the hash is stored in the database. That's the KEY MOMENT, you don't need to decode a stored password and compare the plain.
P.S Every time hashing something with the same salt gives you the
same result.
// Pseudo code
decodeHash(passwordInDatabase) == oldPassword // incorrect + impossible
hash(oldPassword) == passwordInDatabase // correct
If it's equal, back-end hash your new password and update in the database.
Return success or error message from back-end.

Trying to get info from database nodejs / sqlite

I'm doing a login prototype for my app, on the backend I'm trying to recieve the info (email and password) from the frontend to compare with the registered info from the database and validate the user login.
This is what I have done
const infos = request.query;
const email = infos.email as string; // email input frontend
const password = infos.password as string; // password input frontend
const checkLogin = await db('usersLoginsDB') // catching equal from database
.where('usersLoginsDB.email', '=', email)
.where('usersLoginsDB.password', '=', password)
.select('usersLoginsDB.email', 'email')
.select('usersLoginsDB.password', 'password')
After this, I have a checkLogin as type any[] with email and password, and I can't do something like checkLogin.email to use this info.
I tried this and kind of worked
let resultEmail = checkLogin.map(a => a.email) as unknown;
let testEmail = resultEmail as string;
But I'm not sure this is right, I would like to know if there is another way to do it, to get this checkLogin.email to work, or to get the email info from the database in a different way other than using .where and .select.
Thanks in advance.
Probably checkout knex or any other query builder. You don’t need to query everything all at once. You can do this in simple steps:
Query dB to check if the user email exists if it doesn’t throw an error.
If user email exists you need to now verify password. Query the DB to verify it.
If all goes well you can sign in the user. For example, if you have JWT setup send the token back to the user.

Should I need to refresh the page in order to get user data?

Right now I have to refresh the page in order to get user data from local storage, I'm not sure that this is the correct way of doing things. How would I go about getting user data on login? Right now I'm just grabbing it from a local storage JWT but that doesn't seem like best practice. Should I be getting the user based on their ID every time they log in? How do I persist that data?
reload(){
this.router.navigate(['/']);
}
login(userName: string, email: string, password: string) {
const authData: LoginData = {userName, email, password };
console.log('AuthData', authData);
this.http.post<{token: string, expiresIn: number, displayName: string}>(this.loginRoute, authData)
.subscribe(response => {
const token = response.token;
this.token = token;
if (token) {
const expiresInDuration = response.expiresIn;
this.setAuthTimer(expiresInDuration);
this.isAuthenticated = true;
this.username = response.displayName;
this.authStatus.next(true);
const now = new Date();
const expirationDate = new Date(now.getTime() + expiresInDuration * 1000);
console.log(expirationDate);
this.saveAuthData(token, expirationDate, userName);
this.reload();
}
});
}
I'd like to be able to display the user data on my home page of the user that is currently logged in but am unsure how to persist that data. right now I'm just using local storage.
Without a backend, you don't have a ton of options (like websockets). But with what you have you could create a separate function from login that checks if the user is actually authenticated and put it in an interval and poll it every X seconds. This will get rid of the reload function. You would create an Observable onInit that just goes off every 10 seconds or whatever and checks the status of the token.
Cheers!

Resources