TypeError: User.comparePassword is not a function, can't login or register users - passport.js

I am attempting to compare password that the user registers with and then logins with and get the following error:
TypeError: User.comparePassword is not a function
at /home/ubuntu/workspace/app.js:51:10
at Query.<anonymous> (/home/ubuntu/workspace/node_modules/mongoose/lib/model.js:4038:16)
at /home/ubuntu/workspace/node_modules/kareem/index.js:273:21
at /home/ubuntu/workspace/node_modules/kareem/index.js:131:16
at _combinedTickCallback (internal/process/next_tick.js:73:7)
at process._tickCallback (internal/process/next_tick.js:104:9)
User model where comparePassword() located
var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");
var bcrypt = require("bcrypt-nodejs");
var UserSchema = new mongoose.Schema({
username: {type: String, unique: true, required: true},
password: {type: String, unique: true, required: true},
// avatar: String,
firstName: String,
lastName: String,
email: {type: String, unique: true, required: true},
resetPasswordToken: String,
resetPasswordExpires: Date,
// isAdmin: {type: Boolean, default: false}
});
UserSchema.plugin(passportLocalMongoose);
//save function -- hashes and then saves the password
UserSchema.pre('save', function(next) {
var user = this;
var SALT_FACTOR = 5;
if (!user.isModified('password')) return next();
bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) return next(err);
user.password = hash;
next();
});
});
});
//compares and checks password
UserSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
module.exports = mongoose.model("User", UserSchema);
Here is the app.js file it gets passed to:
var express = require("express"),
app = express(),
bodyParser = require("body-parser"),
mongoose = require("mongoose"),
expressSanitizer = require("express-sanitizer"),
passport = require("passport"),
cookieParser = require("cookie-parser"),
LocalStrategy = require("passport-local"),
session = require("express-session"),
nodemailer = require("nodemailer"),
bcrypt = require("bcrypt-nodejs"),
async = require("async"),
crypto = require("crypto"),
flash = require("connect-flash"),
moment = require("moment"),
User = require("./models/user"),
// seedDB = require("./seeds"),
methodOverride = require("method-override");
// APP CONFIG
mongoose.connect("mongodb://localhost/blog", {useMongoClient: true});
//PRODUCTION CONFIG - LIVE URL GOES HERE!
app.set("view engine", "ejs");
app.use(express.static(__dirname + "/assets"));
app.use(bodyParser.urlencoded({extended: true}));
app.use(expressSanitizer());
app.use(methodOverride("_method"));
app.use(cookieParser('secret'));
//require moment
app.locals.moment = require('moment');
// seedDB(); //seed test data!
// PASSPORT CONFIGURATION
app.use(require("express-session")({
secret: "It's a secret to everyone!!",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
passport.use(new LocalStrategy(function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) return done(err);
if (!user) return done(null, false, { message: 'Incorrect username.' });
console.log(user);
User.comparePassword(password, function(err, isMatch) {
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Incorrect password.' });
}
});
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
app.use(function(req, res, next){
res.locals.currentUser = req.user;
res.locals.success = req.flash('success');
res.locals.error = req.flash('error');
next();
});
Whenever I register or try to sign in I get the comparePassword is not a function error.
here is my mongo db of one of the examples of a user called "apple"
{ "_id" : ObjectId("5a2ec9ff5e64680bdd930330"), "salt" : "398d81d059440847480ae8f31c20c3e21c3a1b522ba57dbeed6f8cfa9fddf1b7", "hash" : "3270b089ac910853e07c40bdb1e71a948ccfee378b9d92936a50f53f6f4e02f79bdb6cc611c0e15a5c8dbdded8c165b30c86597ce202d47af89859ef074f093ecc4bfe9419fe406ee067ba60fdc97acda4934ae95160b29a9e6fa57b288ddad7c4fe84e459d165256084c8af9399d0258f93d24068e03e0b5cef704c3c115b78944c98b8c431bdb3d6314803f30af318f135316277050f71897f0009e1509da0634fe1bbf6c80bfb66a9d175061544e28d33f7573780e7be576db409b8950fb79d63b40aa34bea8919bf81afd46bddd866e1eab4fa447ba26af137f4bf01559b6632084b9d04677671f85b9b5ec4fe4eaf088c1023fb512a93e546dcab1571ec02217594cba2894a5468f41b4935f976d79587d47f16bb4ef00566ded4a0b349cda6f442fd5b11e325e1feb651d3c367b5943d22ed32e6ec14065bea58cb6fbd55bf13daab2b183bb6ab59f150a64b99304ce7b163621753d69377cc394c8d5ec9c2e30ef6ab50f32e8241c542a01950d3e16c1d83aa6986a67aac96e9652270919bd05e6994493b89326857bf9ac75a450a39ba79f2af0035354e40bd32c43f9acc7e660158ca17e3d7e66673ab8950a63126fa0f6f7206de71bef0c3f212022b5f6eaee49775c2750e8ac068b98566bf6fe643c935e6dac531b9a5b12ace19c850ec369f431432b7a2d0817097f3762244ee8db1f30c0814bcbcaf8b6ab77c", "username" : "apple", "firstName" : "apple", "email" : "apple#gmail.com", "password" : "$2a$05$lkr5htZwOBVSIMJSQXveduEKmx6/AnN7jUxTjDSDKr3UQ0DqaQzrS", "__v" : 0 }
>

Try using lower case user:
user.comparePassword(password, function(err, isMatch) {
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Incorrect password.' });
}
});

Related

How can I add the feature of 'Change Password' in nodejs with passport authentication and passport-local package?

This is the authentication passport code I have put it in passport.js:
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
passport.serializeUser(function (user, done) {
done(null, user._id);
});
passport.deserializeUser(function (id, done) {
User.findOne({_id: id}, function (err, user) {
done(err, user);
})
});
passport.use(new LocalStrategy({
usernameField: 'email'
},
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 or password'
});
}
if (!user.validPassword(password)) {
return done(null, false, {
message: 'Incorrect username or password'
});
}
return done(null, user);
})
}
));
And this is the Schema code I am storing password in the form of salt and hash in mongoDB using mongoose queries:
var mongoose = require('mongoose');
var crypto = require('crypto');
var userSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true
},
name: {
type: String,
required: true
},
hash: String,
salt: String
});
userSchema.methods.setPassword = function(password) {
this.salt = crypto.randomBytes(16).toString('hex');
this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64, 'sha1').toString('hex');
};
userSchema.methods.validPassword = function(password) {
var hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64, 'sha1').toString('hex');
return this.hash === hash;
};
module.exports = mongoose.model('User', userSchema);
This is the auth.js code for Login route:
var express = require('express');
var router = express.Router();
var passport = require('passport');
var mkdirp = require('mkdirp');
var nodemailer = require('nodemailer');
var config = require('../config');
var transporter = nodemailer.createTransport(config.mailer);
router.route('/login')
.get(function(req, res, next) {
res.render('login', { title: 'Login your account'});
})
.post(passport.authenticate('local', {
failureRedirect: '/login'
}), function (req, res) {
res.redirect('/profile');
})
And below is the password change route I am trying to execute which is possibly not working. What I think is I need to update the hash and salt value into the database while user changes password successfully which I am unable to figure out how can I do it. Please help!!!!!!!!!!!!!!!
router.post('/api/changePassword', function(req,res,next){
req.checkBody('oldPass', 'Empty Password').notEmpty();
req.checkBody('newPass', 'Password do not match').equals(req.body.confirmPass).notEmpty();
var errors = req.validationErrors();
if (errors) {
console.log("Errors hain bhai");
console.log(errors);
res.render('settingsClient', {
oldPass: req.body.oldPass,
newPass: req.body.newPass,
confirmPass:req.body.confirmPass,
errorMessages: errors
});
}
else {
User.findOne({id: req.user.id}, function (err, data) {
console.log("came inside api changePassword else condition inside User.findOne");
if (err) {
console.log(err);
}
else {
data.setPassword(req.body.newPass, function(err,datas){
if(datas) {
data.save(function (err,datass) {
if (err) {
res.render('settingsClient', {errorMessages: err});
} else {
console.log("Hash and Salt saved");
}
});
}
else {
console.log("setPassword error"+ err);
}
});
}
})
}
})
Its not working/not updating the password values into the database. What could be the possible reason and mistake I might be doing?
Yeah! So I removed the callback funtion from the setPassword and tried with the promises way/route to solve this query:
Here I am posting the solution which is working fine now.
User.findOne(userObj).then(function(sanitizedUser) {
if (sanitizedUser) {
console.log("sanitizedUser.hash = "+ sanitizedUser.hash);
sanitizedUser.setPassword(req.body.newPass);
console.log("Password going to be changed successfully now")
sanitizedUser.save(function (err,data) {
if (err) {
res.render('register', {errorMessages: err});
} else {
console.log("Password changed successfully");
res.send('Password reset successful');
}
});
} else {
res.send('user does not exist');
}
}, function(err) {
console.error(err);
});

Express/Passport creating session but not sending cookie to front end

right now I have a react and node.js project running.
Client side(react app) - http://localhost:3000
Server side(Node.js) - http:..localhost:5000
I am currently trying to implement the user authentication session. So far, it will send the username, password, and email(when registering) to the server. The server will then parse the data and attempt to register/login the user. The user credential is stored in an MongoDB atlas database. If it is successful, it will send the info back to the server.
After a successful authentication, the server is supposed to create a session and cookie pair. The session will be stored and the cookie will be sent to the client. However, the latter part isn't happening. I know the session is being created successfully as it is stored in another database in the MongoDB, but no matter what I do, I can't seem to get the cookie to the front end.
UserModel
const mongoose = require('mongoose');
const uniqueValidator = require('mongoose-unique-validator');
const passportLocalMongoose = require('passport-local-mongoose');
const bcrypt = require('bcrypt');
const SALT_WORK_FACTOR = 10;
const userSchema = new mongoose.Schema({
username:{
type: String,
lowercase:true,
unique: true,
required:[true, 'Username is required'],
match:[/^[a-zA-Z0-9]+$/, 'is an invalid username'],
index: true
},
password:{
type: String,
required:[true, 'Password is required']
},
email:{
type:String,
lowercase:true,
unique:true,
required:[true, 'Email is required'],
match:[/\S+#\S+\.\S+/, 'is an invalid email'],
index: true,
uniqueCaseInsensitive: true
}
}, {timestap: true})
userSchema.plugin(uniqueValidator, {message: '{PATH} is already taken.'});
//encrypt the password
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 using our new salt
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
console.log("hashedPassword stored");
user.password = hash;
next();
});
});
});
//validatePassword
userSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
userSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('users', userSchema, 'userInfo');
authRouter.js
router.post("/register-login",
//this section checks the authentication
(req, res, next) =>{
passport.authenticate('local'
,
{ successRedirect: '/',
failureRedirect: '/listingsForm'
}
,
//this will be called if authenticate was successful
(err, user, info) => {
if(req.body.isSignUp){
if(err){
return res.status(400).json({errors:err});
}
if(!user){
return res.status(400).json({errors:info});
}
else{
return res.status(200).json({success: `created ${user.username}`});
}
}
else{
if(err){
return res.status(400).json({errors:err});
}
if(!user){
return res.status(400).json({errors:info});
}
else{
console.log(user.id);
req.login(user, (err)=>{
if(err){
throw err;
}
});
return res.status(200).json({success:`Welcome back ${user.username}`});
}
}
})(req,res,next)
}
authUser.js
const User = require('../schemes/User')
const passport = require('passport');
const LocalStrategy = require('passport-local');
passport.serializeUser((user,done) =>{
console.log(user.id);
done(null,user.id);
})
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
passport.use(
new LocalStrategy(
{
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true
},
(req, username, password, done) =>{
// console.log(username, password);
console.log(req.body);
//For Register
if(req.body.isSignUp){
//determine it is a register attempt
const newUser = new User({
username: username,
password: password,
email: req.body.email
});
newUser.save()
.then(
user => {
return done(null,user);
}
)
.catch(
err => {
console.log('there is error');
console.log(err);
return done(null, false, {message:err.message});
}
)
}
//For Login
else{
User.findOne({username: username})
.then(user => {
let attemptPassword = password;
if(!user){
return done(null, false, {message:'This username/password does not exist'})
}
else{
console.log("will verify now");
user.comparePassword(attemptPassword, function(err, isMatch) {
if (err){
console.log('hihi');
return done(null, false, {message:err})
}
if(!isMatch){
return done(null, false, {message:'This username/password does not exist'})
}
return done(null, user), {message:'Successfully Logged In'};
});
}
})
}
}
));
module.exports = passport;
Index.js
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
//expression session
app.use(session({
secret: 'secret',
resave: false,
saveUninitialized: true,
store: new MongoStore({mongooseConnection:mongoose.connection})
}))
app.use(passport.initialize());
app.use(passport.session());
//express-router
const authRouter = require('./routes/auth-router');
app.use('/users',authRouter);
server.listen(PORT, () => console.log(`Server has started on port ${PORT}`));
I guess app.use(cors({credentials:true})); will solve your problem, gyus.

passport js 'Missing credentials'

this is my signup strategy...
passport.use('local-signup', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
}, function (req, email, password, done) {
var e = req.body.email;
var name= req.body.name;
var phone = req.body.phone;
var classc = req.body.classc;
var graduated = req.body.graduated;
var major = req.body.major;
var minor = req.body.minor;
var linkedin = req.body.linkedin;
var bio = req.body.bio;
log.console("TESTINGTESTING1");
User.findOne({'email': email}, function (err, user) {
if (err)ß
{
log.console("TESTINGTESTING2");
return done(null);
}
if (user)
{
log.console("TESTINGTESTING3");
return done(null, false, req.flash('signupMessage', 'existed email'));
}
log.console("TESTINGTESTING4");
const newUser = new User();
newUser.email = email;
newUser.password = newUser.generateHash(password);
newUser.name = name;
newUser.phone = phone;
newUser.classc = classc;
newUser.graduated = graduated;
newUser.major = major;
newUser.minor = minor;
newUser.linkedin = linkedin;
newUser.bio = bio;
log.console("TESTINGTESTING5");
newUser.save(function (err) {
if (err)
{
log.console("TESTINGTESTING7");
throw err;
}
log.console("TESTINGTESTING8");
return done(null, newUser);
});
})
}));
it always send failureRedirect so i put to find out the error..
router.post('/register', function(req, res, next) {
console.log(req.url);
passport.authenticate('local-signin', function(err, user, info) {
console.log("authenticate");
console.log(err);
console.log(user);
console.log(info);
console.log(req.body);
res.send(JSON.stringify(info)).status(200)
})(req, res, next);
});
This is what i got for output, it says Missing Credentials... What should I do for fix it ? I already put bodyparser things on my middleware, and tested extended with both false and true.. but still i am getting missing credentials.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
authenticate
null
false
{ message: 'Missing credentials' }
{ email: 'scott2008#google.com',
password: [ 'abcde', 'abcde' ],
name: 'adfa',
phone: '3242523422',
classc: '2014',
graudated: 'on',
major: 'African and African American Studies',
minor: 'Biomedical Engineering',
linkedin: 'adfas',
bio: 'adfaf' }

Node JS Authentications with passport-jwt unauthorized

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

Passport in node.js

Ok, I edit the question, Can someone help me with fix this code, to work??
I am fallow the guide with youtube about creating login system with using passport, I dont understand here about function
passport.use(new LocalStrategy(
When is she called, And from where it gets an argument login, password.
USER MODEL
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
login: { type: String, maxlength: 20, required: false },
password: { type: String, maxlength: 202, required: false },
updated: { type: Date, default: Date.now },
created: { type: Date, default: Date.now }
});
module.exports = mongoose.model('User', UserSchema);
module.exports.comparePassword = function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
if(err) throw err;
callback(null, isMatch);
});
}
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}
User Model
var express = require('express');
var MongoClient = require('mongodb').MongoClient;
var router = express.Router();
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var Product = require('../../model/product.model');
var Category = require('../../model/category.model');
var mongoose = require('mongoose');
var User = require('../../model/user.model');
var _ = require('lodash');
var bcrypt = require('bcrypt');
const saltRounds = 10;
router.post('/create', function (req, res, next) {
var newUser = {
login: req.body.login,
password: req.body.password
}
console.log(req.body)
req.checkBody('login', 'Login is required').notEmpty();
req.checkBody('password', 'Password is required').notEmpty();
var errors = req.validationErrors();
if (errors) {
console.log(errors)
res.send(errors);
} else {
bcrypt.hash(newUser.password, saltRounds, function (err, hash) {
if (err) {
console.log(err)
} else {
newUser.password = hash;
var user = new User(newUser);
user.save()
.then(function (User) {
res.send(User);
})
}
});
req.flash('success_msg', 'You are registered and can now login');
//res.redirect('/');
}
});
passport.use(new LocalStrategy(
function(login, password, done) {
User.findOne({ login: login }, function(err, user){
if(err) {
console.log(err);
}
if(!user) {
return done(null, false, {
message: 'Unkown User'
})
}
});
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'});
}
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.getUserById(id, function(err, user) {
done(err, user);
});
});
router.post('/login', function (req, res, next) {
console.log(req.body)
passport.authenticate('local', { successRedirect: '/', failureRedirect: '/', failureFlash: true }),
function (req, res) {
res.redirect('/product/list')
}
})
module.exports = router;
When is she called?
passport.use(new LocalStrategy( will be used when you call passport.authenticate('local'..
In your project it is while getting post request with '/login'.
LocalStrategy provides strategy to authenticate requests. You can choose different strategy. Strategies, and their configuration, are supplied via the use() function. More information: Passport Documentation
From where it gets an argument login, password?
By default, LocalStrategy expects to find credentials in parameters named username and password. If your site prefers to name these fields differently, options are available to change the defaults.
From LocalStrategy Documentataion

Resources