convert passport usage from mongoose to sequelize - node.js

I am building an app that requires authentication during login and for that I have used passport(passport-local). The app does not run for the login part and the last option available after removing all syntax errors is that the way in which I am using passport(as given in their docs) is for mongoose while I am using sequelize. Can someone please tell how to rectify my passport.js file so that it works fine for sequelize as well?
(using mysql through sequelize; database is already populated)
here is my passport.js file
const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy
const Users = require('./db').Users
module.exports = function (passport) {
console.log("passport is working");
passport.serializeUser(function (users, done) {
console.log("Serialize");
done(null, users.id)
})
function findById(id, fn) {
User.findOne(id).exec(function (err, user) {
if (err) {
return fn(null, null);
} else {
return fn(null, user);
}
});
}
passport.deserializeUser(function (id, done) {
console.log("DeSerialize");
findById(id, function (err, user) {
console.log(user);
done(err, user);
});
})
passport.use(new LocalStrategy(
function (username, password, done) {
Users.findOne({ where: { username: username } },
function (err, users) {
if (err) { return done(err); }
if (!users) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!users.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, users);
});
}
));
}
and here is my db.js file
const Sequelize = require('sequelize');
const db = new Sequelize(
'mydb',
'myuser',
'mypass',
{
dialect: 'mysql',
host: 'localhost',
pool: {
max: 8,
min: 0,
aquire: 30000,
idle: 10000
},
}
);
const Users = db.define('users', {
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
username: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
},
password: {
type: Sequelize.STRING,
allowNull: false,
}
})
db.sync().then(() => console.log("Database is ready"))
exports = module.exports = {
db,
Users
}
also,as a side note,can I do a res.send after post request using passport.authenticate?
app.post('/login', passport.authenticate('local'),
function(req, res) {
res.send({
url:'/profile',
username:req.body.username
});
});
thanks in advance!!

findById has been replaced by findByPk method in Sequelize 4.
http://docs.sequelizejs.com/class/lib/model.js~Model.html#static-method-findByPk

Yes, you need to change the functions where you fetch your user. This:
function findById(id, fn)
Should be something like:
function findById(id, fn) {
User.findById(id).then((user) {
return fn(null, user);
});
}
And you also need to change this a bit:
passport.use(new LocalStrategy(
function (username, password, done) {
Users.findOne({ where: { username: username } })
.then((user) => {
if (!user) {
return done(null, false, { message: '' });
}
// validate your password**
return done(null, user);
});
}
));
If you want to use a function like user.validatePassword() you will need to define an instance method (check sequelize docs) or here
.

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.

Why does sequelize return 500 [object SequelizeInstance:Users] when connecting to express session?

The Problem:
I'm trying to authenticate a user with passport-local strategy. I can successfully retrieve the user from the database, but when I try to redirect to ' / ' and initiate a new session, my server responds with 500 [object SequelizeInstance:Users]
Context:
I came across the 'connect-session-sequelize' node package and implemented it in my app.js:
const db = require('./models/db.js');
const userController = require('./controllers/user');
const myStore = new SequelizeStore({
db: db.sequelize,
table: 'Sessions'
});
app.use(cookieParser());
app.use(session({
secret: process.env.SESSION_SECRET,
store: myStore,
resave: false, // per the express-session docs this should be set to false
proxy: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.get('/login', userController.getLogin);
app.post('/login', userController.postLogin);
app.get('/signup', userController.getSignup);
app.post('/signup', userController.postSignup);
db.sequelize.sync({
force: false,
}).then(() => {
app.listen(app.get('port'), () => {
console.log('%s App is running at http://localhost:%d in %s mode', chalk.green('✓'), app.get('port'), app.get('env'));
});
});
My routes handling the requests:
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const db = require('../models/db.js');
const User = db.user;
passport.serializeUser((user, done) => {
console.log('serializing user: ', user);
done(null, user);
});
passport.deserializeUser((id, done) => {
User.findById(id).then((user) => {
done(user);
});
});
passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
User.findOne({
where: { email: email.toLowerCase() },
}).then((user) => {
if (!user) {
return done(null, false, { msg: `Email ${email} not found.` });
}
user.comparePassword(password, (err, isMatch) => {
if (err) { return done(err); }
if (isMatch) {
return done(null, user);
}
return done(null, false, { msg: 'Invalid email or password.' });
});
});
}));
exports.postSignup = (req, res, next) => {
const errors = req.validationErrors();
const user = new User({
username: req.body.name,
email: req.body.email,
password: req.body.password
});
User.findOne({
where: { email: req.body.email }
}).then((existingUser) => {
if (existingUser) {
req.flash('errors', { msg: 'Account with that email address already exists.' });
return res.redirect('/signup');
}
user.save((err) => {
req.logIn(user, (err) => {
req.session.save(() => res.redirect('/'));
});
});
});
};
'Users' DB model:
const bcrypt = require('bcrypt-nodejs');
module.exports = (sequelize, DataTypes) => {
const Users = sequelize.define('Users', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
allowNull: false,
unique: true
},
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password_hash: {
type: DataTypes.STRING
},
password: {
type: DataTypes.VIRTUAL,
allowNull: false,
unique: false,
set(value) {
const that = this;
bcrypt.genSalt(10, (err, salt) => {
if (err) { return console.log('BCRYPT GEN SALT ERR:', err); }
bcrypt.hash(value, salt, null, (error, hash) => {
if (error) { return console.log('BCRYPT HASH ERR:', err); }
console.log('--> SEQ: BCRYPT hash SET', hash);
that.setDataValue('password', value);
that.setDataValue('password_hash', hash);
});
});
}
}
});
Users.prototype.comparePassword = function comparePassword(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password_hash, (err, isMatch) => {
cb(err, isMatch);
});
};
return Users;
};
'Sessions' DB model:
module.exports = (sequelize, DataTypes) => sequelize
.define('Sessions', {
sid: {
type: DataTypes.STRING,
primaryKey: true
},
userId: DataTypes.STRING,
expires: DataTypes.DATE,
data: DataTypes.STRING(50000),
});
Server Response:
POST /login 302 234.474 ms - 46
Executing (default): SELECT "sid", "userId", "expires", "data", "createdAt",
"updatedAt" FROM "Sessions" AS "Sessions" WHERE "Sessions"."sid" =
'Jhmo9YA9MhwKEVa6zWxvvRQGdYoXmdSQ';
Executing (default): SELECT "id", "username", "email", "password_hash", "phone",
"age", "gender", "location", "createdAt", "updatedAt" FROM "Users" AS "Users"
WHERE "Users"."id" = 'c40d4cd6-4937-4a66-b785-d302e9fa6c40';
Executing (default): UPDATE "Sessions" SET "expires"='2018-05-10 06:31:42.797
+00:00',"updatedAt"='2018-05-09 06:31:42.797 +00:00' WHERE "sid" =
'Jhmo9YA9MhwKEVa6zWxvvRQGdYoXmdSQ'
[object SequelizeInstance:Users]
GET / 500 5.420 ms - -
The deserializeUser should call done with error first:
passport.serializeUser((user, done) => {
console.log('serializing user: ', user.id);
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then((user) => {
done(null, user);
}).catch(done);
});

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

Why is my sequelize model undefined only when I try to use find functions?

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

Resources