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.
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'm a hobbyist coder, and I can usually solve errors with lots of searches, but this one I can't get it right.
when I hit my logout route it throws an error: Cast to ObjectId failed for value "logout" at path "_id" for model "Spot"
I tried mongoose version 4.7.2, it's not working. I can't imagine why is it associating my logout route with the spot model at all.
my app.js
var express = require("express"),
bodyParser = require("body-parser"),
mongoose = require("mongoose"),
passport = require("passport"),
passportFacebook = require("passport-facebook").Strategy,
User = require("./models/user.js"),
Spot = require("./models/spot.js");
mongoose.connect("mongodb://localhost/biketrialspots", { useNewUrlParser: true });
var app = express();
app.set("view engine","ejs");
app.use(bodyParser.urlencoded({extended:true}));
app.use(express.static('public'));
app.use(require("express-session")({
secret: "some secret",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(function(req, res, next){
res.locals.currentUser = req.user;
next();
});
passport.use(new passportFacebook({
clientID: "some id",
clientSecret: "some secret",
callbackURL: "somewebsite/auth/facebook/callback",
profileFields: ['id', 'displayName', 'picture.type(large)']
}, function(accessToken, refreshToken, profile, done) {
User.findOrCreate(profile, function(err, user) {
if (err)
{ return done(err); }
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);
});
});
app.get("/", function(req, res){
Spot.find({}, function(err, spots){
if(err){
console.log(err);
} else{
res.render("index", {spots:spots});
}
});
});
app.get("/new", ensureAuthenticated, function(req, res){
res.render("new");
});
app.post("/", function(req, res){
Spot.create(req.body.spot, function(err, result){
if(err){
console.log(err);
} else{
res.redirect("/");
}
});
});
app.get("/:id", function(req, res){
Spot.findById(req.params.id, function(err, spot){
if(err){
console.log(err);
} else{
res.render("spot", {spot: spot});
}
});
});
// Redirect the user to Facebook for authentication. When complete,
// Facebook will redirect the user back to the application at
// /auth/facebook/callback
app.get('/auth/facebook', passport.authenticate('facebook'));
// Facebook will redirect the user to this URL after approval. Finish the
// authentication process by attempting to obtain an access token. If
// access was granted, the user will be logged in. Otherwise,
// authentication has failed.
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { successRedirect: '/',
failureRedirect: '/login' }));
app.get("/logout", function(req, res){
req.user.logout();
res.redirect('/');
});
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
return res.redirect('/');
}
app.listen(process.env.PORT, process.env.IP, function(req, res){
console.log("APP HAS STARTED!!!!!");
});
user model
var mongoose = require("mongoose");
var userSchema = new mongoose.Schema({
facebookID:Number,
username:String,
photo:String
});
userSchema.statics.findOrCreate = function findOrCreate(profile, cb){
var userObj = new this();
this.findOne({facebookID : profile.id},function(err,result){
if(!result){
userObj.facebookID = profile.id;
userObj.username = profile.displayName;
userObj.photo = profile.photos[0].value;
userObj.save(cb);
} else{
cb(err,result);
}
});
};
module.exports = mongoose.model("User", userSchema);
Thank you
Because app.get("/:id", ...) is written before app.get("/logout", ...) in your code, I guess the request handler of /:id would be called when you get /logout. Then, req.params.id becomes "logout" and the error is thrown by Spot.findById().
How about trying to write app.get("/logout", ...) before app.get("/:id", ...)?
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.
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.
Im using NodeJS, ExpressJS, Mongoose, passportJS & connect-ensure-login. Authenticating users works perfectly.
....
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy
, ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn;
var app = express();
...
app.use(passport.initialize());
app.use(passport.session());
...
passport.use(new LocalStrategy({usernameField: 'email', passwordField: 'password'},
function(email, password, done) {
User.findOne({ 'email': email, 'password': password },
{'_id': 1, 'email':1}, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
return done(null, user);
});
}));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
app.post('/login', passport.authenticate('local',
{ successReturnToOrRedirect: '/home', failureRedirect: '/login' }));
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
Now, I want to add restrictions to some routes to be accessible only by admin. How can I do that? e.g. /admin/*
var schema = new mongoose.Schema({
name: String,
email: String,
password: String,
isAdmin: { type: Boolean, default: false }
});
mongoose.model('User', schema);
Any hint? Thanks
You could attach a custom middleware to the /admin/* route that would check for admin status before passing the request on the any of the more specific /admin/ routes:
var ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn;
...
var requiresAdmin = function() {
return [
ensureLoggedIn('/login'),
function(req, res, next) {
if (req.user && req.user.isAdmin === true)
next();
else
res.send(401, 'Unauthorized');
}
]
};
app.all('/admin/*', requiresAdmin());
app.get('/admin/', ...);
//Add following function to your app.js above **app.use(app.router)** call;
//This function will be called every time when the server receive request.
app.use(function (req, res, next) {
if (req.isAuthenticated || req.isAuthenticated())
{
var currentUrl = req.originalUrl || req.url;
//Check wheather req.user has access to the above URL
//If req.user don't have access then redirect the user
// to home page or login page
res.redirect('HOME PAGE URL');
}
next();
});
I have not tried it but i think it will work.