Search code examples
javascriptnode.jssequelize.jspassport.jspassport-local

login using sequelize and passport in nodejs


I am creating a self sustained chat app project with user authentication.The following code is able to signup (add new users) successfully.However login of previous signed up users does not occur. Example: upon entering the username as foo and password as foopass,this is what happens inside the terminal

 Executing (default): CREATE TABLE IF NOT EXISTS `users` (`id` INTEGER auto_increment , `email` VARCHAR(255) NOT NULL DEFAULT 'abhinav@gmail.com' UNIQUE, `username` VARCHAR(255) NOT NULL UNIQUE, `password` VARCHAR(255) NOT NULL, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing (default): SHOW INDEX FROM `users`
Database is ready
Executing (default): SELECT `id`, `email`, `username`, `password`, `createdAt`, `updatedAt` FROM `users` AS `users` WHERE `users`.`username` = 'foo' LIMIT 1;

the following are my js files

1.server.js

const express = require('express')
const session = require('express-session')
const passport = require('passport')
const path = require('path')
const http = require('http')

const app = express()
const server = http.createServer(app)
const cookieparser = require('cookie-parser')
const bodyParser   = require('body-parser');
app.set('views',path.join(__dirname , 'chat'));

app.set('view engine','html');
var port = process.env.PORT || 5898;
app.use('/', express.static(path.join(__dirname, 'intro')));
app.use('/profile', express.static(path.join(__dirname, 'chat')));
require('./passport')(passport);
require('./app/routes.js')(app, passport);
app.use(express.json());
app.use(session({
    secret: 'project-session',
    resave: true,
    saveUninitialized:true
}))

app.use(passport.initialize())
app.use(passport.session())

server.listen(port, () => console.log("Server running on http://localhost:5898"))

here intro is the public file(accessible to everyone i.e main page) while chat is the private file(accessible to only logged in users)

2.app/routes.js

const express = require('express')
const app = express();
const route = express.Router()
const path = require('path');
const bodyParser = require('body-parser');
const passport = require('passport-local');
const Users = require('../db').Users;
module.exports = function(app,passport)
{

app.use((bodyParser.urlencoded({ extended: false })))
app.use(bodyParser.json());
 app.get('/signup', function(req,res) {
     res.render('../intro/index');
 }) 
 app.get('/login',function(req,res){
     res.send({
         username: req.body.username
     })
 }) 

app.post('/signup', function(req,res){
    var a = [req.body.email,req.body.username,req.body.password];
    Users.findOne({
       where: { email: a[0],
        username: a[1],
        password: a[2],
       }

    }).then(users => {
        if (users) {
        res.send({
            url:'/'
        })
        }
        else{
            Users.create({
                email: a[0],
                username: a[1],
                password: a[2],

          }).then(users => {
                res.send({
                    url:'/profile',
                    username: a[1]
                })
          })    
        }
    })


})
app.post('/login', passport.authenticate('local'),
function(req, res) {
    res.send({
        url:'/profile',
        username:req.body.username
    });
  });

app.get('/logout', function(req, res){
    req.logout();
    res.redirect('/');
  });
}

3.db.js(using mysql)

const Sequelize = require('sequelize');

const db = new Sequelize(
    'socializedb',
    'soc',
    'socpass',

    {
        dialect: 'mysql',
        host: 'localhost',
        operatorsAliases: false,
    }
);

const Users = db.define('users', {
    id: {
        type: Sequelize.INTEGER,
        autoIncrement: true,
        primaryKey: true,
    },
    email: {
        type: Sequelize.STRING,
        allowNull: false,
        unique: true,
        defaultValue: "abhinav@gmail.com"
    },
    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
}

4.passport.js(where my local strategy resides)

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) {
    return done(null, users.id);
    console.log("Serialize");

})

passport.deserializeUser(function (id, done) {
    console.log("DeSerialize");
    Users.findById(id).then((users) => {
        console.log(users);
        return done(null, users);
    });
})

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.password === password) {
            return done(null, false, { message: 'Incorrect password.' });
            }
            return done(null, users);
        });
    }
));

}

I have used local authentication.I am sure it is not a problem with my database as I can see the entries inside the tables residing on my machine.I mostly think the issue could be the use of serialize/deserialize in passport.js.Thanks in advance.


Solution

  • The code is having two problems related to Login.

    1. Sequelize Querying works with Promises. The Sequelize Query in passport's Local Strategy callback is trying to use callbacks instead of Promises. So,

    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.password === password) {
                    return done(null, false, { message: 'Incorrect password.' });
                }
                return done(null, users);
            });
        }
    ));
    

    must be changed to

    passport.use(new LocalStrategy(
        function (username, password, done) {
            Users.findOne({ where: { username: username } })
                 .then(function (users) {
                     if (!users) {
                         return done(null, false, { message: 'Incorrect username.' });
                     }
                     if (!users.password === password) {
                         return done(null, false, { message: 'Incorrect password.' });
                     }
                     return done(null, users);
                 })
                 .catch(err => done(err));
        }
    ));
    

    This should hopefully solve the current error.


    2. Another problem is in server.js, the router functions must be called after the Passport's initialization to initialize the Passport before actually trying to use them. Otherwise, the login and other routes would try to use Passport before passport's initialization middlewares, causing errors. So,

    require('./app/routes.js')(app, passport);
    

    must be moved after

    app.use(passport.initialize())
    app.use(passport.session())
    


    These two fixes should enable the User Login.