I'm pretty new with node.js and I'm trying to implement simple user registration and login form using Node.js, Express, bcrypt, express-session and mongoose.
Whenever the user log in, I want to set the value of req.session.userID to user's id. When I trace the code I can't find the problem. I followed up the tutorial in this link and everything seems to be similar.
Schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt');
var userSchema = new Schema({
teamName: {
type: String,
unique: true,
trim: true,
required: true
},
faculty: {
type: String,
required: true
},
email: {
required: true,
unique: true,
trim: true,
type: String
},
password: {
required: true,
type: String
},
score: {
type: Number,
default: 0
}
});
userSchema.pre('save', function(next) {
var user = this;
bcrypt.hash(user.password, 10, function(err, hash) {
if (err) return next(err)
user.password = hash;
next();
});
});
userSchema.statics.authenticate = (email, password, callback) => {
userModel.findOne({email: email}, (err, user) => {
if (err) return callback(err);
else if (!user) {
console.log('User not found!')
}
else {
bcrypt.compare(password, user.password, (err, result) => {
if (result) {
callback(null, true)
}
else {
return callback()
}
})
}
})
}
var userModel = mongoose.model('User', userSchema);
module.exports = userModel;
server:
var userModel = require('./../models/users');
router.post('/login', (req, res) => {
var email = req.body.email;
var password = req.body.password;
userModel.authenticate(email, password, (err, user) => {
console.log(user)
if (err) {
console.log(err)
}
else if (!user) {
console.log('Wrong Password')
}
else {
req.session.userId = user._id;
console.log(req.session.userId);
}
})
});
Where I have logged the value of req.session.userId it returns undefined! Where is the problem?
The problem is that the callback is returning TRUE. the callback should be returning the user data. callback(null, user)
bcrypt.compare(password, user.password, (err, result) => {
if (result) {
callback(null, true)
}
Should be
bcrypt.compare(password, user.password, (err, result) => {
if (result) {
callback(null, user)
}
Related
I've been starting to add user authentication into my app and when adding the login route, i've been getting the error "TypeError: cb is not a function". I know it is coming from my login route as all my other routes work fine.
I have tried researching the issue and trying a few fixes that i've found but none have worked. So i'm starting to believe i've messed up somewhere and I can't find where.
Login Route:
router.post('/login', function (req, res) {
User.findOne({ username: req.body.username }, function (err, user) {
if (err || user == null) {
console.log(err);
res.redirect('/login');
}
if (!user.comparePassword(req.body.password)) {
req.flash('invalidDetails', 'Wrong username or password!');
res.redirect('/login');
} else {
req.session.userId = user._id;
req.flash('loggedIn', 'User ' + req.body.username + ' has been logged in!');
return res.redirect('/');
}
});
});
User Model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
bcrypt = require('bcrypt'),
SALT_WORK_FACTOR = 10;
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
trim: true
},
username: {
type: String,
unique: true,
required: true,
trim: true
},
password: {
type: String,
required: true
}
});
UserSchema.statics.authenticate = function (email, password, callback) {
User.findOne({ email: email }).exec(function (err, user) {
if (err) {
return callback(err);
} else if (!user) {
var err = new Error('User not found.');
err.status = 401;
return callback(err);
}
bcrypt.compare(password, hash, function (err, result) {
if (result === true) {
return callback(null, user);
} else {
return callback();
}
});
});
};
UserSchema.pre('save', function (next) {
var user = this;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return next(err);
// hash the password along with our new salt
bcrypt.hash(user.password, salt, function (err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
});
UserSchema.methods.comparePassword = function comparePassword(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function (err, isMatch) {
if (err) {
return cb(err);
}
cb(null, isMatch);
});
};
var User = mongoose.model('User', UserSchema);
module.exports = User;
I'm expecting it to compare the password with the password that has been entered into the login form with the password that is stored in the database and log in the user if the password is correct or redirect back to the login page with the invalidDetails flash message if the password is incorrect.
But what i'm actually getting is the "TypeError: cb is not a function" error when trying to login.
You are not passing a callback that's why.
If you want a promise based method you can write something like this
AuthSchema.methods.comparePassword = function(candidatePassword) {
const currentPassword = this.password;
return new Promise((resolve, reject) => {
bcrypt.compare(candidatePassword, currentPassword, function(err, isMatch) {
if (err) return reject(err);
resolve(isMatch);
});
})
};
And now you can call that with await
I'm currently working on a new project and I'm trying to get the login route working. But the login always fails because the User.findOne() method is always returning as null.
I've tried changing the export from the usermodel to
var User = mongoose.model('User', UserSchema, 'User');
But it hasn't change anything.
I know the connection to the database is fine because the register route works fine and saves correctly.
Login Route
router.post('/login', function (req, res) {
User.findOne({ username: req.body.username, password: req.body.password }, function (err, user) {
if (err || user == null) {
res.redirect('/login');
console.log(user);
} else {
req.session.userId = user._id;
res.redirect('/');
}
});
});
User Schema
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
trim: true
},
username: {
type: String,
unique: true,
required: true,
trim: true
},
password: {
type: String,
required: true
}
});
UserSchema.statics.authenticate = function (email, password, callback) {
User.findOne({ email: email }).exec(function (err, user) {
if (err) {
return callback(err);
} else if (!user) {
var err = new Error('User not found.');
err.status = 401;
return callback(err);
}
bcrypt.compare(password, hash, function (err, result) {
if (result === true) {
return callback(null, user);
} else {
return callback();
}
});
});
};
//hashing a password before saving it to the database
UserSchema.pre('save', function (next) {
var user = this;
bcrypt.hash(user.password, 10, function (err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
var User = mongoose.model('users', UserSchema);
module.exports = User;
Database Connection
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/wowDb', { useNewUrlParser: true });
var db = mongoose.connection;
mongoose.set('useCreateIndex', true);
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function () { });
I used
db.users.findOne({username: "Imortalshard"});
and got the output
{
"_id" : ObjectId("5cc10cd13361880abc767d78"),
"email" : "admin#wowdb.com",
"username" : "Imortalshard",
"password" : "$2b$10$7Ln5yHFqzPw/Xz6bAW84SOVhw7.c0A1mve7Y00tTdaKzxzTph5IWS",
"__v" : 0
}
Console output from console.log(req.body) and console.log(user)
I'm waiting the user that i registered to be able to successfully login, but currently it is just redirecting me back to the login page and giving me a null reading for user in the console.
Im trying to setup my Node JS API.
I have a User model :
// Dependencies
var restful = require('node-restful');
var mongoose = restful.mongoose;
var bcrypt = require('bcrypt');
// Schema
var userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true},
firstname: {
type: String,
required: true
},
lastname: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true,
lowercase: true
},
password: {
type: String,
required: true},
},
{
timestamps: true
});
// Saves the user's password hashed
userSchema.pre('save', function (next) {
var user = this;
if (this.isModified('password') || this.isNew) {
bcrypt.genSalt(10, function (err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
} else {
return next();
}
});
// Use bcrypt to compare passwords
userSchema.methods.comparePassword = function(pw, cb) {
bcrypt.compare(pw, this.password, function(err, isMatch) {
if (err) {
return cb(err);
}
cb(null, isMatch);
});
};
module.exports = restful.model('Users', userSchema);
I want to use passport with jwt for authentication :
// Dependencies
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var config = require('../config/database');
// Load models
var User = require('../models/user');
// Logique d'authentification JWT
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('JWT');
opts.secretOrKey = config.secret;
opts.audience = 'localhost';
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findById(jwt_payload._id, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
Company.findById(jwt_payload._id, function(err, company) {
if (err) {
return done(err, false);
}
if (company) {
done(null, company);
} else {
done(null, false)
}
});
}));
};
And my route for authentication :
// User
router.post('/users/login', (req, res) => {
User.findOne({
email: req.body.email
}, (err, user) => {
if (err) throw err;
if (!user) {
res.json({success: false, message: 'Authentication failed. User not found.'});
} else {
// Check if passwords matches
user.comparePassword(req.body.password, (err, isMatch) => {
if (isMatch && !err) {
// Create token if the password matched and no error was thrown
var token = jwt.sign(user, config.secret, {
expiresIn: 10080 // in seconds
});
res.json({success: true, token: 'JWT ' + token, user: {
id: user._id,
username: user.username,
email: user.email
}});
} else {
res.json({success: false, message: 'Authentication failed. Passwords did not match.'});
}
});
}
});
});
Everything work great on postman.
The token is correctly generated and signed with user's informations.
But i have a problem with the authentication on a protected route :
router.get('/users/profile', passport.authenticate('jwt', { session: false }), function(req, res) {
res.send('It worked! User id is: ' + req.user._id + '.');
});
Everytime, it gives me an "Unauthorized 401" Error.
I really dont know where is the problem, i think the problem is around jwtFromRequest, i also tried with Bearer but it also doesn't work...
I think a good option to avoid this kind of problems is to start from a base project that uses this authentication strategy, and after you have that working, modify it with your functionality.
Here you have an example with jwt authentication strategy and Refresh token implementation: https://solidgeargroup.com/refresh-token-autenticacion-jwt-implementacion-nodejs?lang=es
I'm trying to set up a user login and registration with the ORM Sequelize in Node. I have the registration part working fine. My model instance is defined as User, and when I call User.createUser, my model function works and a user is added to the db. However, when I try to use my getUserNameById function in my passport local strategy, I get an error "User is not defined". I've been stuck for over a day trying to figure this out.
Model file (I've imported sequelize, bcrypt, and created a connection)
var Users = connection.define("Users", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: Sequelize.STRING
},
username: {
type: Sequelize.STRING
},
account: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
},
}, {
timestamps: false
});
Users.sync();
connection.authenticate()
.then(function () {
console.log("CONNECTED! ");
})
.catch(function (err) {
console.log("MYSQL ERROR: FAILED TO CONNECT");
})
.done();
module.exports = function (connection, DataTypes) {
return Users;
}
module.exports.createUser = function (newUser) {
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(newUser.password, salt, function(err, hash) {
newUser.password = hash;
Users.create(newUser).then(function (Users){
console.dir(Users.get());
})
});
});
}
module.exports.getUserByUsername = function (username) {
return Users.find({
where: {username: username}
}).then(function (user) {}, function (err) {
console.log(err);
});
}
module.exports.comparePassword = function(candidatePassword, hash, done, user){
bcrypt.compare(password, hash, function(err, isMatch){
if (err) console.log(err)
if (isMatch) {
return done(null, user)
} else {
return done(null, false)
}
});
}
module.exports.getUserById = function(id, callback){
Users.findAll({
where: {id: id}
});
}
My route code, specifically the local passport strategy I'm having trouble with
var User = require("../models/users");
var express = require('express');
var router = express.Router();
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function (username, password, done) {
User.getUserByUsername(username, function (user) {
if(!user){
return done(null, false, {message:'Incorrect username'})
}
})
User.comparePassword(password, user.password, function (err, isMatch) {
if(err) throw err;
if(isMatch){
return done(null, user);
}else{
return done(null, false, {message: 'Invalid password'});
}
})
}
));
Your ./models/user class is exporting a function that takes the connection and DataTypes as arguments, but you aren't passing them in - the connection should be passed through. You shouldn't need to call connection.authenticate().
// new db connection
var connection = new Sequelize(
schema,
username,
password,
{
host: host,
dialect: dialect,
}
)
var DataTypes = // define DataTypes
// pass into User
var User = require("../models/users")(connection, DataTypes);
I have an issue that I am not getting an idea that why user stored object does not return password in validatePassword function in model/user.js. I followed all steps described in passportjs official documentation.
I used localstategy of passportjs for signin. When I compare email it always compare but when I tried to execute validate password and use this.password or as a argument it always blank and that is why my password is not compare.
I got all user schema information but I does not get password in user object so I am not able to compare it.
Can anyone tell how could I get out of this issue?
Console Log
root#wk11:/var/www/html/mytripmean/trunk# nodejs server.js
Mytrip is listening on port 1000
MongoDB connection successful
---- User Information ----
myemail#gmail.com
Password##123
{ message: 'Incorrect password.' }
not user:
false
[Error: Illegal arguments: string, undefined]
/var/www/html/mytripmean/trunk/app/data/models/user.js:105
throw err;
^
Error: Illegal arguments: string, undefined
at Error (<anonymous>)
at Object.bcrypt.compare (/var/www/html/mytripmean/trunk/node_modules/bcryptjs/dist/bcrypt.js:250:42)
at model.userSchema.methods.validPassword (/var/www/html/mytripmean/trunk/app/data/models/user.js:102:12)
at Query.<anonymous> (/var/www/html/mytripmean/trunk/app/data/routes/user.js:222:27)
at /var/www/html/mytripmean/trunk/node_modules/kareem/index.js:177:19
at /var/www/html/mytripmean/trunk/node_modules/kareem/index.js:109:16
at process._tickCallback (node.js:448:13)
models/user.js
var mongoose = require('mongoose')
, Schema = mongoose.Schema
, ObjectId = Schema.ObjectId
, randtoken = require('rand-token')
, bcrypt = require("bcryptjs");
var userSchema = new Schema({
// _Id: objectId,
social_id: {
type: String, //(Social id of facebook/twitter)
required: false,
unique: false
},
social_media: {
type: String, //(facebook/twitter)
required: false,
unique: false
},
link_id: {
type: String, //will be dispalying as user reference in url
required: true,
unique: true
},
nick_name: {
type: String, // Unique Nickname for signup
required: true,
unique: true
},
email: {
type: String, // Unqiue Email for signup
required: true,
unique: true
},
password: {
type: String, // Password
required: true,
select: false
},
user_type: {
type: Number, // 1: SuperAdmin, 2: Admin, 3: SiteUser, 4: Restaurant
required: true
}, //reason_to_close: String, // Close Account
is_active: {
type: Number, // -1: pending to activation, 0: inactive, 1: active,
required: true
},
is_close: {
type: Number, // -1: pending to close/Undecided, 0: closed , 1: open/ not close,
required: true
},
is_online: {
type: Number, // 0: Offline, 1: Online
required: true
},
created_at: {
type: Date,
default: Date.now
}, // Registration date
updated_at: {
type: Date, // Registration activation date / user update date
default: Date.now
}
}, {collection: 'user'});
// Password verification
userSchema.methods.validPassword = function (candidatePassword, callback) {
bcrypt.compare(candidatePassword, this.password, function (err, isMatch) {
console.log(err);
if (err) {
throw err;
}
callback(null, isMatch);
});
};
var User = module.exports = mongoose.model("User", userSchema);
module.exports.checkEmail = function (callback) {
return this.model('User').count({email: this.email}, callback);
};
module.exports.validateEmailOrNickname = function (username, callback) {
var orCondition = [{nick_name: username}, {email: username}];
//return this.model("user").findOne().or(orCondition);
return this.model("User").find({$or: orCondition}, callback);
};
module.exports.getUserById = function (id) {
User.findById(id, callback);
};
module.exports.createUser = function (user, callback) {
bcrypt.genSalt(10, function (err, salt) {
bcrypt.hash(user.password, salt, function (err, hash) {
user.password = hash;
user.save(callback);
});
});
};
routes/user.js
var express = require('express');
var router = express.Router();
var bcrypt = require("bcryptjs")
var User = require('../models/user');
var UserProfile = require('../models/userProfile');
var UserSignupToken = require('../models/userSignupToken.js');
var IpLogger = require('../models/ipLogger.js');
var passport = require("passport");
var localStrategy = require("passport-local"), Startegy;
router
.route('/api/user/register')
.post(
function (req, res, next) {
var user_, userData_;
userData_ = {
link_id: req.body.manLinkId,
nick_name: req.body.txtNickname,
email: req.body.txtEmail,
password: req.body.manPassword,
user_type: req.body.manUserType,
is_active: req.body.manIsActive,
is_close: req.body.manIsClose,
is_online: req.body.manIsOnline
};
user_ = new User(userData_);
user_.validate(function (err) {
if (err) {
} else {
//check recaptch is validate or not
var request = require('request');
request
.post({
url: 'http://www.google.com/recaptcha/api/verify',
form: {
privatekey: process.env.RECAPTCHA_PRIVATE_KEY,
remoteip: req.connection.remoteAddress,
challenge: req.body.captcha.challenge,
response: req.body.captcha.response
}
}, function (err, httpResponse, body) {
if (body.match(/false/) === null) {
//Recaptcha validated
User.createUser(user_, function (err, data) {
if (err) {
console.log("stpe 1:");
console.log(err);
res.json({status: 0, message: 'User having an error on stage 1'});
} else {
res.locals.user = data;
//res.json({error:1, message: 'User saved'});
next();
}
});
//res.json({ "captchaError": true });
} else {
res.json({"captchaError": false});
}
});
}
});
},
function (req, res, next) {
var userProfileData_, userProfile_;
userProfileData_ = {
user_id: res.locals.user.id,
link_id: res.locals.user.link_id,
full_name: req.body.txtFullname,
is_active: -1
};
userProfile_ = new UserProfile(userProfileData_);
userProfile_.save(function (err, data) {
if (err) {
console.log("stpe 2:");
console.log(err);
res.json({status: 0, message: 'User having an error on stage 2'});
} else {
//res.json({error:1, message: 'User profile generated'});
next();
}
});
},
function (req, res, next) {
var userSignupTokenData_, userSignupToken_;
userSignupTokenData_ = {
user_id: res.locals.user.id,
link_id: res.locals.user.link_id,
is_active: -1
};
userSignupToken_ = new UserSignupToken(userSignupTokenData_);
userSignupToken_.save(function (err, data) {
if (err) {
console.log("stpe 3:");
console.log(err);
res.json({status: 0, message: 'User having an error on stage 3'});
} else {
//res.json({error:1, message: 'User signup token generated'});
next();
}
});
},
function (req, res, next) {
var ipLoggerData_, ipLogger_, client_IP;
ipLoggerData_ = {
user_id: res.locals.user.id,
link_id: res.locals.user.link_id,
client_ip: req.ip,
activity: "signup"
};
ipLogger_ = new IpLogger(ipLoggerData_);
ipLogger_.save(function (err, data) {
if (err) {
console.log("stpe 4:");
console.log(err);
res.json({status: 0, message: 'User having an error on stage 4'});
} else {
res.json({status: 1, message: 'user saved'});
}
});
}
);
//Check unique validation
router
.route('/api/user/authenticate')
.post(
function (req, res, next) {
console.log("---- User Information ----");
console.log(req.body.txtSigninEmail);
console.log(req.body.txtSigninPassword);
passport.authenticate('local', function (err, user, info) {
console.log(info);
if (err) {
console.log(err);
return next(err);
}
if (!user) {
console.log("not user:");
console.log(user);
return res.status(401).json({
err: info
});
}
req.login(user, function (err) {
if (err) {
return res.status(500).json({
err: 'could not login user'
});
}
res.status(200).json({
status: 'login successful'
});
});
})(req, res, next);
});
router
.route('/api/user/checkEmail')
.post(
function (req, res) {
User.count({email: req.body.txtSigninPassword}, function (err, user) {
if (err) {
// console.log("error false");
res.json(false);
} else {
// console.log("data");
// console.log(user);
res.json({"status": user > 0 ? false : true});
}
});
});
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
passport.use('local', new localStrategy(
{
usernameField: 'txtSigninEmail',
passwordField: 'txtSigninPassword'
},
function (username, password, done) {
User.findOne({email: username}, function (err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {message: 'Incorrect username.'});
}
if (!user.validPassword(password)) {
return done(null, false, {message: 'Incorrect password.'});
}
return done(null, user);
});
}
));
module.exports = router;
After 2 hours of efforts I found answer of my question. In my User model password field, I set property "select:false", due to that I always get a password as blank.
Older:
var userSchema = new Schema({
password: {
type: String, // Password
required: true,
select: false
},
}
After re-setting select: true it works fine.
Updated:
var userSchema = new Schema({
password: {
type: String, // Password
required: true,
select: true
},
}