I am trying to implement the login function in my express API. Below are my controller and the model functions related to the authentication process.
auth.controller
import User from '../models/user.model';
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';
dotenv.config({path: './src/.env'});
const jwtSecret = process.env.JWT_SECRET;
function login(req, res, next) {
var username = req.body.username;
var password = req.body.password;
User.login(username, password)
.then(user => {
if (user) {
const token = jwt.sign({
userId: user._id
}, jwtSecret);
res.json({
token,
username: user.userName
});
}
res.send('No such user exists!');
})
.catch(e => next(e));
}
export default { login };
user.model
userSchema.statics = {
list() {
return this.find().exec();
},
login(username, password) {
var queryPromise = this.findOne({ "userName": username }).exec();
queryPromise.then(function(user) {
bcrypt.compare(password, user.password, function(err, isMatch){
if (err) throw err;
console.log('Is password match :', isMatch);
if (isMatch) {
return;
}
});
});
}
}
Here's the error I'm getting
TypeError: Cannot read property 'then' of undefined
Can someone tell me what I'm doing wrong here?
The login method should return a Promise if you want to use .then
login(username, password) {
return new Promise((resolve, reject) => {
var queryPromise = this.findOne({ "userName": username }).exec();
queryPromise.then(function(user) {
bcrypt.compare(password, user.password, function(err, isMatch) {
if (err) throw err;
console.log('Is password match :', isMatch);
if (isMatch) {
return resolve(user); // pass the user if you want to access it in the .then statement
} else {
return reject(new Error('Invalid Credentials'));
}
});
});
})
}
Related
I am trying to make a login route in node.js but when ever I enter wrong credential my application crashes
This is my Login route, Please tell me what I am doing wrong
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({ email: req.body.email });
!user && res.status(400).json("User Not exist!");
const validated = await bcrypt.compare(req.body.password, user.password);
!validated && res.status(400).json("Invalid Password!");
const { password, ...others } = user._doc;
res.status(200).json(others);
} catch (err) {
res.status(500).json(err);
}
});
Can you just add a return before the responses and make your code cleaner the problem is that when you write res.status(400)... the code will continue to the next steps and the app will crash so your code should be like that
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({ email: req.body.email });
if (!user) {
return res.status(400).json("User Not exist!");
}
const validated = await bcrypt.compare(req.body.password, user.password);
if (!validated) {
return res.status(400).json("Invalid Password!");
}
const { password, ...others } = user._doc;
return res.status(200).json(others);
} catch (err) {
return res.status(500).json(err);
}
});
this is the controller and if I do it whitout hashing it works but with hashing it give me an error and I cannot handle in catch(err)
this is the controller and if I do it whitout hashing it works but with hashing it give me an error and I cannot handle in catch(err)
this is the controller and if I do it whitout hashing it works but with hashing it give me an error and I cannot handle in catch(err)
const { throws } = require("assert");
const { bcrypt } = require("bcryptjs");
const User = require("../models/user");
// #desc register new user
// #routes POST /users/register
exports.createUser = async (req, res) => {
const errors = [];
try {
await User.userValidation(req.body);
const { fullname, email, password } = req.body;
const user = await User.findOne({ email });
if (user) {
errors.push({ message: "کاربری با این ایمیل موجود است" });
return res.render("register", {
pageTittle: "صحفه ثبت نام",
path: "/register",
errors: errors,
});
}
bcrypt.genSalt(10, (err, salt) => {
if (err) throw err;
console.log(err);
bcrypt.hash(password, salt, async (err, hash) => {
if (err) throw err;
await User.create({
fullname,
email,
password: hash,
});
res.redirect("/users/login");
});
});
} catch (err) {
err.inner.forEach((e) => {
errors.push({
name: e.path,
message: e.message,
});
});
}
return res.render("register", {
pageTittle: "صحفه ثبت نام",
path: "/register",
errors: errors,
});
};
I have this route
router.patch("/me/update-password", authenticate, (req, res) =>{
let newPassword = _.pick(req.body, 'password');
const newP = newPassword.password;
User.findByCredentials(req.user.username, req.user.password).then((user) => {
user.password = newP;
user.save().then(() => {
res.send(user);
}).catch((e) => {
res.status(400).send(e);
});
});
});
/////////////////////////////////////////////////////////////////////////////
UserSchema.statics.findByCredentials = function (username, password) {
var User = this;
return User.findOne({username}).then((user) => {
if (!user) {
return Promise.reject();
}
return new Promise((resolve, reject) => {
// Use bcrypt.compare to compare password and user.password
bcrypt.compare(password, user.password, (err, res) => {
if (res) {
resolve(user);
} else {
reject();
}
});
});
});
};
//////////////////////////////////////////////////////////////////
UserSchema.pre('save', function (next) {
let user = this;
if (user.isModified('password')) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(user.password, salt, (err, hash) => {
user.password = hash;
next();
});
});
} else {
next();
}
});
I find the username and password of the authenticated user and then I want to send a new password which I save as newP.
I want to update the password hash and salt it and then save, so I tried with the method user.save() which hashes and salts the password. But when I send a patch request it wont`t finish.
Does anybody know why ?
I tried everything but I am stuck.
I am using Bcrypt for hashing passwords and storing it in the database,
Bcrypt is returning false, when I retrieve the hashed password from the database, and compare with the typed user password.
I am using it under Promise, but bcrypt is returning false on the correct password
Here is the code:
userSchema.statics.findByCredentials = function(email, password) {
const User = this;
return User.findOne({email}).then((user) => {
if(!user) {
return Promise.reject();
}
// console.log(user.password);
return new Promise((resolve, reject) => {
bcrypt.compare(password, user.password, (err,res) => {
if(res) {
resolve(user);
}
else {
reject("Problem here");
}
console.log(res);
});
});
});
};
I am trying promise chain to the main file i.e server.js and return the details to the user, but it's not working.
Here is the route code of express:
app.post('/users/login', (req, res) => {
const body = _.pick(req.body, ['email', 'password']);
// res.send(body);
User.findByCredentials(body.email, body.password)
.then((user) => {
res.send(user);
}).catch((e) => res.send(e));
});
Thanks
Have you tried using the promise api for bcrypt?
It might look like this:
userSchema.statics.findByCredentials = function(email, password) {
const User = this;
return User.findOne({email}).then((user) => {
if(!user) {
return Promise.reject();
}
// console.log(user.password);
return bcrypt.compare(password, user.password)
.then(res => {
if (res) {
return user;
}
throw new Error('Problem here');
});
});
}
If not, you might be getting an error, so you could check the err argument to see if something came back from there. Other than that, as long as user.password is the hashed version of the original password, then it should be working.
Hi All,
I am authenticating my user using bcrypt module.
I am able to do perform the Registration process, but facing problem during Login process.
User Model:
var userSchema = new Schema({
email: {type: String, required: true},
password: {type: String,
});
Hashing methods:
userSchema.methods.encryptPassword = function (password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(5), null)
};
userSchema.methods.validPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};
Sign in:
module.exports.login = function (user, callback) {
User.findOne({'email': user.email, 'password': user.validPassword(this.password)}, callback);
};
Login Route
router.post('/login', function (req, res) {
var user = req.body;
User.login(user, function (err, user) {
if (err) {
throw err;
}
if (!user) {
res.sendStatus(404);
return;
}
res.json(user.id);
});
});
While executing am getting this error: TypeError:user.validPassword is not a function
Please Help.
Your mistake is that the user being provided to your login method is not a Mongoose DB object. Instead, your login function should look something like this:
module.exports.login = function (request, callback) {
User.findOne({'email': request.email }, function(err, user) {
if (err) return callback(err);
if(!user || !user.validPassword(request.password)) return callback();
return callback(null, user);
});
};
This will ensure that user is a valid Mongoose object before you attempt to verify the password.
One other possible solution, if you'd prefer to avoid checking that the password is valid in your data layer, is to simply fetch the user document based on its email and then check the password in the login route.
router.post('/login', function (req, res) {
var user = req.body;
User.findOne(user, function (err, user) {
if (err) {
throw err;
}
if (!user) {
res.sendStatus(404);
return;
}
if (!user.validPassword(req.body.password)) {
res.sendStatus(401);
return;
}
res.json(user.id);
});
});
In Login Route, you need to instantiate the Schema:
router.post('/login', function (req, res) {
var user = new User(req.body);
User.login(user, function (err, user) {
if (err) {
throw err;
}
if (!user) {
res.sendStatus(404);
return;
}
res.json(user.id);
});
});