How to check currentPassword to allow user to change Password - node.js

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.

Related

How to verify a hashed secret without knowing the salt?

I store the API-Keys as hashes in a database.
...
async function createToken(userId:number) {
...
const salt=await bcrypt.genSalt(15)
const hash=await bcrypt.hash(token, salt)
await db.store({userId,hash})
}
async function verifyToken(token:string){
const userId= //I don't know the UserID since only the token is sent as HTTP header
const hashes= db.get(userId)
for(const hash of hashes) {
if(await bcrypt.compare(token, hash)) {
return true
}
}
return false
}
How do I verify the token validity without knowing the userID?
The only option I see is to loop through all DB records, and try if the produced hash for that record matches. But this results potentially in thousands of hashes checked before I find the right one.
I may reduce this to only the tokens belonging to a specific UserID. However, this would require my users to also send there userID which I don't want them to do.
Seems like you are using bcrypt hashes as token to authenticate the users, but that's not the actual purpose of such hashes. If you need to use a token, why not use something like JWT. It can hold the user information, and can be verified without a single db call.

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)

How to update already existing data using token only?

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

Storing hash of username using ASP.NET identity

I'm writing an application which needs to have personally identifiable information removed/absent at all times from the database. Given that someone may use their real name in their username, and that an email address may be present in their AspUserIdentity records, I have decided one solution might be to hash these values. In simple terms: when someone logs in with a username, I hash the username they entered and see if that hash exists in the database; if it does, then I log them in. This is easy to do and works just fine by modifying the Login and Register methods in the AccountController. But now I am left with no knowledge of the entered username...
I could just store the username in session, but that seems jankety. What I'd like to do is to update the cookie that gets sent down upon successful login to use the username they entered (and not the hashed value stored in the DB). That way User.Identity.GetUserName() returns the plain text username (and not the hashed username). To the client the process ought to be transparent (and to me as the programmer too).
The question is: how? What's the best place to do this? I'm still relatively green when it comes to the latest ASP.NET Identity stuff. I see in Startup.Auth there's a lot of juicy stuff related to cookies, but I don't see anywhere I can modify the cookie itself upon login and prior to it being sent down.
Is all of this deep within Owin itself?
Thanks in advance,
When user logs in and you compare the hash of username, you can add their real username as a claim to the identity. This is serialised into cookie and available with the user on every request, but not persisted in a DB:
public async Task SignIn(string userName, string password, bool rememberMe)
{
var hashedUsername = getMyHash(username)
var loggedInUser = await userManager.FindAsync(hashedUsername, password);
if (loggedInUser == null)
{
// failed to login
return FailedToLogin(); // whatever you do there
}
// Ok, from now on we have user who provided correct username and password.
// and because correct username/password was given, we reset count for incorrect logins. This is for user lockout
await userManager.ResetAccessFailedCountAsync(loggedInUser.Id);
if (!loggedInUser.EmailConfirmed)
{
return EmailIsNotConfirmed(); // email is not confirmed - display a message
}
if (await userManager.IsLockedOutAsync(loggedInUser.Id))
{
return UserLockedOut(); // user is locked out - display a message
}
var identity = await userManager.CreateIdentityAsync(loggedInUser);
identity.AddClaim(new Claim("OriginalUsername", originalUsername));
var authenticationManager = context.GetOwinContext().Authentication;
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = rememberMe }, identity);
//TODO redirect to a home page
}
Then when you need to display an actual username, not a hash do this:
public static String GetOriginalUsername(this IPrincipal principal)
{
if (principal == null)
{
return String.Empty;
}
var claimsPrincipal = principal as ClaimsPrincipal;
if (claimsPrincipal == null)
{
return String.Empty;
}
var originalUsernameClaim = principal.Claims.SingleOrDefault(c => c.Type == "OriginalUsername");
if (originalUsernameClaim == null)
{
return String.Empty;
}
return originalUsernameClaim.Value;
}
And call this method on User.GetOriginalUsername() in *.cshtml files or in Controllers. Or HttpContext.Current.User.GetOriginalUsername() if you need it somewhere else.

How to Decrypt an encrypted Password in Node.js

I want to create a change password page for user. I encrypt the password when I save the user in Database (mongodb).
User.virtual('password')
.set(function(password) {
this._password = password;
this.salt = this.makeSalt();
this.hashed_password = this.encryptPassword(password);
})
.get(function() { return this._password; });
User.method('authenticate', function(plainText) {
return this.encryptPassword(plainText) === this.hashed_password;
});
User.method('makeSalt', function() {
return Math.round((new Date().valueOf() * Math.random())) + '';
});
User.method('encryptPassword', function(password) {
return crypto.createHmac('sha1', this.salt).update(password).digest('hex');
});
I don't know how to decrypt it in order to get the original password back. any help will be appreciated.
The password is hashed, not encrypted, and you can't get the original back -- that's the whole point of hashing, it's a one-way function. You shouldn't ever need to get the original back, as you have no legitimate use for it. To validate a user, you hash the password that they give you in the same way as the stored one, then compare the hashes.
I think the best solution here would be to allow the user to answer some security questions and then be able to reset the password by clicking a link sent to the email in their profile. They might end up setting it to the same password, but that is not your concern. This allows you to not having to worry about unhashing the password.
Of course this is harder if you did not provide for this in the original sign-up form. But if your service isn't actually launched yet, this should be very easy to implement.

Resources