I am trying to build the authentication system using PassportJs and Sequelize. I made the registration system by myself, using Sequelize. I want to use PassportJS only for Login.
It does not redirect me to the failureRedirect route, neither to the SuccessRedirect one, but when submitting the form it enters into an endless loop and in my console, the following message appears:
Executing (default): SELECT `id`, `username`, `lastName`, `password`, `email`, `phone`, `createdAt`, `updatedAt` FROM `user` AS `user` LIMIT 1;
My project is structured in: users_model.js , index.js and users.js (the controller).
The code I have in my index.js looks like this:
//===============Modules=============================
var express = require('express');
var bodyParser = require('body-parser');
var session = require('express-session');
var authentication= require('sequelize-authentication');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var passportlocal= require('passport-local');
var passportsession= require('passport-session');
var User = require('./models/users_model.js');
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.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
console.log(id);
});
});
var users= require('./controllers/users.js');
var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use('/users', users);
app.use('/events', events);
//-------------------------------------------Setup Session------------
app.use(session({
secret: "ceva",
resave:true,
saveUninitialized:true,
cookie:{},
duration: 45 * 60 * 1000,
activeDuration: 15 * 60 * 1000,
}));
// Passport init
app.use(passport.initialize());
app.use(passport.session());
//------------------------------------------------Routes----------
app.get('/', function (req, res) {
res.send('Welcome!');
});
//-------------------------------------Server-------------------
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
In my controller, I made the registration system by myself, using Sequelize. In users.js, I have:
var express = require('express');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var passportlocal= require('passport-local');
var passportsession= require('passport-session');
var router = express.Router();
var User = require('../models/users_model.js');
//____________________Initialize Sequelize____________________
const Sequelize = require("sequelize");
const sequelize = new Sequelize('millesime_admin', 'root', '', {
host: 'localhost',
dialect: 'mysql',
pool: {
max: 5,
min: 0,
idle: 10000
}
});
//________________________________________
router.get('/',function(req,res){
res.send('USERS');
});
router.get('/register', function(req, res) {
res.render('registration', {title: "Register" });
});
router.post('/register', function(req, res) {
var email = req.body.email;
var password = req.body.password;
var username= req.body.username;
var lastname= req.body.lastname;
var phone= req.body.phone;
User.findAll().then(user => {
usersNumber = user.length;
x=usersNumber+1;
var y =usersNumber.toString();
var uid='ORD'+ y;
User.sync().then(function (){
return User.create({
id:uid,
email: email,
password:password,
username: username,
lastName: lastname,
phone: phone,
});
}).then(c => {
console.log("User Created", c.toJSON());
res.redirect('/users');
}).catch(e => console.error(e));
});
});
router.get('/login',function(req,res){
res.render('authentication');
});
//router.post('/login', function(req, res, next) {
// console.log(req.url); // '/login'
// console.log(req.body);
// I got these:{ username: 'username', password: 'parola' }
// passport.authenticate('local', function(err, user, info) {
// console.log("authenticate");
// console.log('error:',err);
// console.log('user:',user);
// console.log('info:',info);
// })(req, res, next);
//});
router.post('/login', passport.authenticate('local', {
successRedirect: '/events',
failureRedirect: '/users/register'
}));
router.get('/logout', function(req, res){
req.logout();
res.redirect('/users/login');
});
//__________________________________________
module.exports = router;
Main problem: not an infinite loop, but incorrect usage of Sequelize
This is not an infinite loop but just a hanging response from the server which would be ended with a timeout error.
When you do this:
passport.use(new LocalStrategy(
function(username, password, done) {
...
}
));
...passport and express wait for the done function to be called. Once it's done(), they go forward in the middleware chain and send the response to the client.
The done function is not called, because Sequelize seems to not support callback functions, but promises. So, the correct way to call Sequelize methods is:
User.findOne({ username: username }).then(user => {
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
done(null, user);
}).catch(err => done(err));
(de)Serializing the session user
Seems that there is no id field in the user instances, but userid. Therefore we have to do:
passport.serializeUser(function(user, done) {
done(null, user.userid);
});
passport.deserializeUser(function(id, done) {
User.findOne({ userid: id }).then(user => {
done(null, user);
console.log(id);
}).catch(err => done(err));
});
For reference, this commit fixes these issues.
Related
I am currently creating a small user authentication app using Node + Express + Passport. When the user logs in, they are rerouted automatically to the index page "/" and a session should be established with passports authentication. For some reason when trying to console.log(req.user), it is returning "undefined".
The authentication with passport seems to be working properly with the post route
app.post("/login", passport.authenticate("local", {
successRedirect: "/home",
failureRedirect: "/login"
}), (req, res) => {
})
But the session is not being established with the user model. I'd like to eventually store the userId in the session. Here is a look at my current set up with user model and passport implementation on the server file.
const mongoose = require("mongoose");
const passportLocalMongoose = require('passport-local-mongoose');
const userSchema = mongoose.Schema({
username: String,
email: String,
password: String
});
userSchema.plugin(passportLocalMongoose);
const user = mongoose.model("User", userSchema);
module.exports = user;
-----------------------------------------------------------------------------------------
const express = require("express"),
mongoose = require("mongoose"),
bodyParser = require("body-parser"),
session = require("express-session"),
User = require("./models/user"),
passport = require('passport'),
LocalStragety = require('passport-local'),
app = express();
mongoose.connect("mongodb://localhost/shopping_cart_app", { useNewUrlParser: true })
.then(console.log("MongoDB Connected"))
.catch(err => console.log(err));
app.set("view engine", "ejs");
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(__dirname + '/views'));
app.use(session({
secret: "secret",
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStragety(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
app.post("/login", passport.authenticate("local", {
successRedirect: "/home",
failureRedirect: "/login"
}), (req, res) => {
})
I've tried looking into Passports config a bit more but on the documentation provided, it states that once passport.authenticate runs, a session with the user is established. Any tips would be greatly appreciate.
Thanks
I know this may seem simple, but have you tried req.body.user?
The req.body contains the data submitted by the user. The documentation suggest that you use a body parser to populate the information because it's undefined by default. However, instead of using the app object I use express router without parsing.
const express = require("express");
const router = express.Router();
router.post("/login", passport.authenticate("local", {
successRedirect: "/home",
failureRedirect: "/login"
}), (req, res) => {
console.log(req.body.user);
})
for more information: req.body
Try this one, In my project, it is working.
LocalStrategy
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;
var mongoose = require('mongoose');
var admins = mongoose.model('admins');
var bCrypt = require('bcrypt-nodejs');
var flash = require('connect-flash');
var moment = require('moment');
// User
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(obj, done) {
console.log("deserializing " + obj);
done(null, obj);
});
passport.use('adminlogin',new LocalStrategy(
function(username, password, done) {
admins.findOne({ 'email' : username },
function(err, user) {
//console.log(username);
if (err)
return done(err);
if (!user){
//console.log('Username '+username+' does not Exist. Pleasr try again.');
return done(null, false, { message: 'Incorrect Username/Password. Please try again.' });
}
if (!isValidPasswordAdmin(user, password)){
//console.log('Invalid Password');
return done(null, false, { message: 'Incorrect Password. Please try again.' });
}
return done(null, user);
}
);
})
);
var isValidPassword = function(user, app_pin){
return bCrypt.compareSync(app_pin, user.app_pin);
}
var isValidPasswordAdmin = function(user, password){
return bCrypt.compareSync(password, user.password);
}
module.exports = passport;
Login Route
router.post('/login', function (req, res, next) {
admins.find({}, function (err, user) {
if (err) {
console.log('internal database error');
req.flash('error', 'Database Error');
res.redirect('/admins');
} else {
passport.authenticate('adminlogin', function (err, user, info) {
if (err) {
console.log(err);
req.flash('error', 'Database Error');
res.redirect('/admins');
} else if (!user) {
req.flash('error', info.message);
res.redirect('/admins');
} else {
req.logIn(user, function (err) {
if (err) {
req.flash('error', 'Database Error');
res.redirect('/admins');
} else {
res.redirect('/admins/home');
}
});
}
})(req, res, next);
}
});
});
I am trying to use postman for the first time and yes i am a beginner with this authentication. I have a register page and a login page in angular and my backend is running on another port which is coded in nodejs. So i have installed passport and other required packages for the login authentication and registering user process. But on sending request to register route for registering a user i get the UnhandledPromiseRejectionWarning error. Even though there is then and catch inside the route it is still giving this error because of which i am confused. The data that i am sending is perfect but the error is occuring on the server side.
Here is my app.js file that contains all the routes
var express = require('express');
var mongoose = require('mongoose');
var bodyparser = require('body-parser');
var cors = require('cors');
var session = require('cookie-session');
var flash = require('connect-flash');
var passport = require('passport');
var bcrypt = require('bcryptjs');
require('./config/passport')(passport);
var User = require('./models/User');
var app = express();
app.use(bodyparser.json());
app.use(cors());
var expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(session({
name: 'session',
keys: ['key1', 'key2'],
cookie: {
secure: true,
httpOnly: true,
domain: 'example.com',
path: 'foo/bar',
expires: expiryDate
}
}))
app.set('port', process.env.port || 3000);
app.use(passport.initialize());
app.use(passport.session());
// Connect flash
app.use(flash());
// Global variables
app.use(function(req, res, next) {
res.locals.success_msg = req.flash('success_msg');
res.locals.error_msg = req.flash('error_msg');
res.locals.error = req.flash('error');
next();
});
var db = mongoose.connect("mongodb://localhost:27017/server", {
useNewUrlParser: true
}, function(err, response) {
if (err) {
console.log('There is error in connecting with mongodb');
}
console.log('Connection has been established.');
});
app.get('/', (req, res) => {
res.send("hello");
});
//Trying registering with passport
app.post('/register', (req, res) => {
console.log(req.body);
debugger;
const { firstname, lastname, email, password } = req.body;
let errors = [];
if (errors.length > 0) {
res.render('register', {
errors,
name,
email,
password,
password2
});
} else {
User.findOne({ email: email }).then(user => {
if (user) {
errors.push({ msg: 'Email already exists' });
res.render('register', {
errors,
firstname,
lastname,
email,
password
});
} else {
const newUser = new User({
firstname,
lastname,
email,
password
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser
.save()
.then(user => {
req.flash(
'success_msg',
'You are now registered and can log in'
);
res.redirect('/login');
})
.catch(err => console.log(err));
});
});
}
});
}
});
//end
app.post('/login', (req, res, next) => {
console.log(req.body);
passport.authenticate('local', {
successRedirect: '/dashboard',
failureRedirect: '/login',
failureFlash: true
})(req, res, next);
});
app.listen(app.get('port'), function(err, response) {
console.log("Server is running");
});
passport.js file in config folder
const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
// Load User model
const User = require('../models/User');
module.exports = function(passport) {
passport.use(
new LocalStrategy({ emailField: 'email' }, (email, password, done) => {
// Match user
User.findOne({
email: email
}).then(user => {
if (!user) {
return done(null, false, { message: 'That email is not registered' });
}
// Match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Password incorrect' });
}
});
});
})
);
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
};
To resolve this specific issue, you need to provide a catch() when you execute User.findOne() at the same level as the outermost then():
User.findOne({ email: email })
.then(user => { /* existing code */})
.catch(err => console.log(err)); // add catch()
And:
// Match user
User.findOne({ email: email })
.then(user => { /* existing code */ })
.catch(err => console.log(err)); // add catch()
Regarding the issue with the engine. You need to specify a rendering engine such as pug or html or ejs, this is something that Express as a framework expects. The link you provided that you are basing this code off of is using a rendering engine, specifically ejs. This is evident from the use of res.render(), which renders templates from the "views" folder.
app.set('view engine', 'ejs');
Even with the rendering engine, set you don't actually need to render any templates. As long as you remove res.render() and instead just use res.send() or equivalent, you can use this Express application as an API.
If your intention is to NOT use templates but still render the built angular files, you need to update the code and remove all instances of res.render() and res.redirect(). You can instead, still setting a view engine to satisfy Express, do something along the following:
// towards top of file
app.use(express.static(path.join(__dirname, 'path/to/built/angular')));
// ...
// after all other routes
app.get('*', (req, res) =>{
res.sendFile(path.join(__dirname+'/path/to/built/angular/index.html'));
});
Hopefully that helps!
hi i'm using express to implements passport, but i don't knows when i'm hit login url i always get error 400 bad request. This is my code :
main.js :
var express = require('express');
var app = express();
require('./config/passport');
require('./config/static')(app, express);
require('./config/routes')(app);
require('./config/seed')();
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
passport.js :
var passport = require('passport');
var User = require('../api/model/user.model');
var LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.verifyPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
routes.js :
var passport = require('passport');
module.exports = function (app) {
app.post('/api/sign_in', passport.authenticate('local'), function (req, res) {
res.json(res.body);
});
app.get('/', function (req, res) {
res.sendFile(path.resolve(__dirname + '/../public/index.html'));
});
};
and when i'm hit url "/api/sign_in" with 'POST' method with parameter '{email: "john#local.com", password: "12345"}', i'm get error bad request 400
I am trying to use passport local auth with sequelize . When I submit login form, the request/respond cycle never end and there is no error message in the terminal .
Here are all of my codes:
app.js
var Sequelize = require('sequelize'),
express = require('express'),
bodyParser = require('body-parser'),
cookieParser = require('cookie-parser'),
passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
User = require('./models/users');
........ and other imports.....
//route import , model injection
var auth = require('./routes/auth')(User);
.......................
app.use(session({
store: new RedisStore(),
secret: 'keyboard cat',
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
console.log(user);
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id).then(function(user){
done(null, user);
}).catch(function(e){
done(e, false);
});
});
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({where: {username: username}}).then(function(err, user) {
if (err) { return done(err); }
if (!user) {
console.log('Incorrect username.');
return done(null, false, { message: 'Incorrect username.' });
} else if (password != user.password) {
console.log('Incorrect password');
return done(null, false, { message: 'Incorrect password.' });
} else {
console.log('ok');
done(null, user);
}
});
}
));
and routes/auth.js :
var express = require('express'),
passport = require('passport');
var routes = function(User) {
var router = express.Router();
// routes for registration
router.route('/register')
.get(function(req, res) {
res.render('register');
})
.post(function(req, res) {
User.count().then(function(number) {
if (number >= 1) {
res.redirect('/auth/login');
} else {
User.create({
username: req.body.username,
password: req.body.password
});
res.redirect('/auth/login');
}
});
});
//routes for login
router.route('/login')
.get(function(req, res) {
res.render('login');
})
.post(function(req, res) {
passport.authenticate('local', { successRedirect: '/dashboard',
failureRedirect: '/auth/login' });
});
return router;
};
module.exports = routes;
Why does the request/response cycle never end?
Your current middleware definition for './login' POST is incorrect and does not send a response, which is why it doesn't end (until it times out).
Instead of calling passport.authenticate in a middleware function, the result of calling passport.authenticate should be used as middleware itself. I suggest the following:
router.route('/login')
.get(function(req, res) {
res.render('login');
})
.post(passport.authenticate('local', { successRedirect: '/dashboard',
failureRedirect: '/auth/login' });
);
See http://passportjs.org/docs/authenticate for an example.
Race condition in registration code
You didn't ask about this, but there is a race condition in your middleware for './register' POST.
User.create returns a promise for saving the created user. Until that promise is resolved there is no guarantee that the user exists in the backing datastore. However, immediately after calling create, your code redirects to the login endpoint which would query the database for that user.
Here is some code that avoids this problem:
User.create({ ... })
.then(function() {
res.redirect('/auth/login');
})
.catch(function(err) {
// Handle rejected promise here
})
The catch is included because it is always good practice to handle rejected promises and thrown exceptions.
I am new to nodejs. I have been trying to create a simple login and register system using mongodb and express. I have created the entire app but with one error:
var express = require('express')
, passport = require('passport')
, util = require('util')
, LocalStrategy = require('passport-local').Strategy;
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var session = require('express-session');
//var flash = require('connect-flash');
var monk = require('monk');
var db = monk('localhost:27017/loginsystem');
var collection = db.get('messagescollection');
function findById(id, fn) {
var collection = db.get('messagescollection');
collection.findOne({ _id: id }).on('success', function (doc) {
fn(null, doc);
});
}
function findByUsername(username, fn) {
collection.findOne({ username: username }).on('success', function(doc) {
return fn(null, doc);
});
return fn(null, null);
}
var app = express();
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
app.use(function(req,res,next){
req.db = db;
next();
});
app.use(cookieParser());
app.use(session({
secret: 'keyboard cat'
hours
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
console.log("user",user);
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
console.log(id,"id");
findById(id, function (err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy(
function(username, password, done) {
findByUsername(username, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Unknown user ' + username }); }
if (user.password != password) { return done(null, false, { message: 'Invalid password' }); }
return done(null, user);
})
}
));
app.use(express.static(__dirname + '/public'));
app.get('/account', ensureAuthenticated, function(req, res){
res.send(req.user);
});
app.get('/login', function(req, res){
res.redirect("/login.html") //redirect back to homepage
});
app.get('/register', function(req, res) {
res.redirect("/register.html") //redirect back to homepage
})
app.post('/login', passport.authenticate('local', { failureRedirect: '/login'}), function(req, res) {
console.log("success",req.user);
res.redirect('/account');
});
app.post('/register', function(req, res) {
console.log(req.body);
// Submit to the DB
collection.insert({
"username" : req.body.username,
"email" : req.body.email,
"password" : req.body.password
}, function (err, doc) {
if (err) {
// If it failed, return error
res.send("There was a problem adding the information to the database.");
}
else {
//res.redirect('/account');
}
});
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
app.listen(9000);
function ensureAuthenticated(req, res, next) {
console.log("req.user",req.user,req.session);
if (req.isAuthenticated()) { return next(); }
res.redirect('/login');
}
When I register, the user is added to the database. And it is redirected to /login but when I login then I get this error:
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11)
at ServerResponse.header (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/express/lib/response.js:700:10)
at ServerResponse.res.location (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/express/lib/response.js:814:8)
at ServerResponse.redirect (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/express/lib/response.js:853:8)
at /home/mareebsiddiqui/SummerOfCode/loginsystem/app.js:130:7
at Layer.handle [as handle_request] (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/express/lib/router/layer.js:82:5)
at next (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/express/lib/router/route.js:110:13)
at complete (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/passport/lib/middleware/authenticate.js:243:13)
at /home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/passport/lib/middleware/authenticate.js:250:15
at pass (/home/mareebsiddiqui/SummerOfCode/loginsystem/node_modules/passport/lib/authenticator.js:427:14)
How could I resolve this issue? Thanks.
EDIT: I know that there are many questions with the same title but there problems are different. This error is caused by many different situations and my situation is different from others. I have a problem of redirects whereas other questions have problems of ending a response.
You should use return response.<method>. For example, return response.redirect().
Also, response.end() might be helpful.