Search code examples
expresspassport.jssequelize.jspassport-local

PassportJS Local Endless Loop on Authentication


I'm trying to figure out where the error lies in my authentication code that is creating an endless loop every time I try to login. I think it is within the local strategy passport.use('local', new LocalStrategy section of my code because my console.log(req.body.email); passes back the correct value that was inputted in the field and a query is logged that uses the findOne value, but none of the other console.log are being triggered (one for a email error, another for password error and one for success).

Example: trying to login with test@test.com email address

test@test.com
Executing (default): SELECT `user_id`, `first_name` AS `firstName`, `last_name` AS `lastName`, `email`, `password`, `createdAt`, `updatedAt` FROM `user` AS `user` LIMIT 1;

user.js:

module.exports = function(sequelize, DataTypes) {

    var User = sequelize.define('user', {
        user_id: {
            type: DataTypes.INTEGER,
            autoIncrement: true,
            primaryKey: true
        },
        firstName: {
            type: DataTypes.STRING,
            field: 'first_name'
        },
        lastName: {
            type: DataTypes.STRING,
            field: 'last_name'
        },
        email: DataTypes.STRING,
        password: DataTypes.STRING,

    }, {
        freezeTableName: true
        });
        return User;
    }

db-index.js:

var Sequelize = require('sequelize');
var path = require('path');
var config = require(path.resolve(__dirname, '..', '..','./config/config.js'));
var sequelize = new Sequelize(config.database, config.username, config.password, {
    host:'localhost',
    port:'3306',
    dialect: 'mysql'
});

sequelize.authenticate().then(function(err) {
    if (!!err) {
        console.log('Unable to connect to the database:', err)
    } else {
        console.log('Connection has been established successfully.')
    }
});

var db = {}


db.User = sequelize.import(__dirname + "/user");

db.sequelize = sequelize;
db.Sequelize = Sequelize;

sequelize.sync();

module.exports = db;

site-routes.js:

var express = require('express');
var siteRoutes  = express.Router();
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var models = require('../models/db-index');

/*====  Passport Configuration  ====*/

// Serialize sessions
passport.serializeUser(function(user, done) {
  done(null, user.id);
});

passport.deserializeUser(function(id, done) {
  model.User.find({where: {id: id}}).success(function(user){
    done(null, user);
  }).error(function(err){
    done(err, null);
  });
});


passport.use('local', new LocalStrategy({
    passReqToCallback : true,
    usernameField: 'email'
    }, 
    function(req, email, password, done) {
        console.log(req.body.email);
        //Find user by email
        models.User.findOne({ email: req.body.email }, function(err, user) {
            //If there is an error, return done

            if (err) { return done(err); }
        //If user does not exist, log error and redirect
      if (!user) {
        console.log('No email')
        return done(null, false, req.flash('message', 'Email not found.'));
      }
        //If user exists, but wrong password
      if (!user.validPassword(password)) {
        console.log('Password fail');
        return done(null, false, { message: 'Incorrect password.' });

      }
        //If all credentials match, return user
        console.log('Attempting login');
      return done(null, user);
      console.log('Successful login');
    });
  }
));



/*====  Index   ====*/

siteRoutes.get('/', function(req, res){
    res.render('pages/index.hbs');
});

/*====  Login   ====*/


siteRoutes.route('/login')

    .get(function(req, res){
        res.render('pages/login.hbs');
    })

    .post(passport.authenticate('local', {
        successRedirect: '/',
        failureRedirect: '/sign-up',
        failureFlash: true
    }));



siteRoutes.route('/sign-up')

    .get(function(req, res){
        res.render('pages/sign-up.hbs');
    })

    .post(function(req, res){

        models.User.create({
            firstName: req.body.firstName,
            lastName: req.body.lastName,
            email: req.body.email,
            password: req.body.password
        }).then(function() { 
        res.redirect('/');
    }).catch(function(error){
        res.send(error);
    })

});


module.exports = siteRoutes;

login.hbs:

<!DOCTYPE html>
<head>
    {{> head}}
</head>
<body>
    {{> navigation}}
    <div class="container">
        <div class="col-md-6 col-md-offset-3">
            <form action="/login" method="post">
                <label for="login-username">Username</label>
                <input type="text" class="form-control" id="login-username"  name="email" placeholder="username or email">
                <br />
                <label for="login-password">Password</label>
                <input type="password" class="form-control" id="login-password"  name="password">
                <div class="login-buttons">
                    <button type="submit">Login</button>
                </div>
            </form>
            <a href="/sign-up">Don't have an account? Then register here!</a>
            <br />
            <a href="#">Forgot your password?</a>
        </div>
    </div>
</body>

Solution

  • You are passing a callback function as a second argument to models.User.findOne, but Sequelize functions don't take callbacks. Instead they return a promise. Your code in site-routes.js should look something like this:

    passport.use('local', new LocalStrategy({
        passReqToCallback: true,
        usernameField: 'email'
      },
      function(req, email, password, done) {
        console.log(req.body.email);
        //Find user by email
        models.User.findOne({
          email: req.body.email
        })
        .then(function(user) {
          // handle login here, user will be falsey if no user found with that email
        })
        .catch(function(err) {
          // either findOne threw an exception or it returned a rejected promise
        });
      }
    ));
    

    For a full-fledged example of local authentication using passport.js and Sequelize you can see my login-fiddle. Here is a perma-link to the passport local authentication strategy.