Here is some testing of code that works: Assume all files are importing bcrypt from bcryptjs and DB is working.
// Validate Password testing
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash("testing", salt);
const plaintextPassword = "testing"; // What users should enter in the login form
const storedHash = hashedPassword; // Stored hash in your database
const isValidPassword2 = await bcrypt.compare(
plaintextPassword,
storedHash
);
console.log("testing password: " + plaintextPassword);
console.log("testing user.password " + storedHash);
console.log(isValidPassword2); // Should be true if plaintextPassword is correct
// end testing
OUTPUT:
testing password: testing
testing user.password $2a$10$k9h8yGCntskaRbPU6cMuPuzovwdn3AUE9JrOx1k74ZYImSAMUnyLa
true
Here is my code following the same logic, but not working:
register/route.js
// Hash Password
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
// Create new user
const newUser = new User({
username,
email,
password: hashedPassword,
});
login/route.js
const isValidPassword = await bcrypt.compare(password, user.password);
console.log("password: " + password);
console.log("user.password " + user.password);
console.log(isValidPassword);
OUTPUT: I'd like to note that for this user, test4 IS the correct password.
password: test4
user.password $2a$10$RtI4wnOtcuJL3N6xpGdFgOrU2ij9aUU4tzTyOwEEX4XpOEmf03iUO
false
User.js
import mongoose from "mongoose";
import bcrypt from "bcryptjs";
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
});
userSchema.pre("save", async function (next) {
if (!this.isModified("password")) {
return next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
});
// Correctly reference the model to prevent overwriting
const User = mongoose.models.Users || mongoose.model("Users", userSchema);
export default User;
I'm really not sure what is going on. Is my user schema rehashing it a second time? But I was told to do it this way to store it in the database. Sorry for the code dump, thanks!
The main problem you are facing is that you are hasing the password twice when you go to save the password to the user, but only hashing it once when you compare it... Therefore all you have to do is remove the extra logic to hash it before creating the user.
Every time you create a user, you will create a salt for that user and hash their password using that salt... According to This SO answer, bcrypt saves the salt with the password which tells it what the salt is. Therefore you don't need to save the salt separately in your database. When the user goes to log in, you will compare their password to their hashed password that is stored with the user. Here's an untested example with your code below:
// Create new user
const newUser = new User({
username,
email,
password: password, //passing plain text password.
}); // We will hash the password when creating it, as well as generate the salt.
import mongoose from "mongoose";
import bcrypt from "bcryptjs";
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
}
});
userSchema.pre("save", async function (next) {
if (!this.isModified("password")) {
return next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt); //password gets first hash here. Salt is saved with it by bcrypt.
next();
});
// Correctly reference the model to prevent overwriting
const User = mongoose.models.Users || mongoose.model("Users", userSchema);
export default User;
const isValidPassword = await bcrypt.compare(password, user.password); //Behind the scenes bcrypt knows to add the salt in user.password to the plaintext password.
You will notice that in the register all we do is create the user with the password, which then will generate the salt. Bcrypt saves that salt as part of the password for later use.
Then when we go to login, we pass the input password to be hashed (and salted) and compared to the password stored in the database (which is already hashed and contains the salt).
Ultimately at the end of the day the problem you are facing is that you hash the password twice.