Abhijeet Chakravorty
Abhijeet Chakravorty

Reputation: 227

User Signup using passport+express+mysql+sequelize

I am trying to build a user signup api using Passport, MySql, NodeJS and Sequelize. The only problem that i face is that when a user has signed up once and he tries to sign up again with the same email user is thrown a 401 Unauthorized Error instead of the user object. When i tried to debug the same the response that i was getting from the server was this [object SequelizeInstance:users]. The files have been mentioned below. Thanks a tonnn in advance!!!.

Passport.js file:

var LocalStrategy = require('passport-local').Strategy;
var mysql = require('mysql');
var Model = require('../models/models.js');

// expose this function to our app using module.exports
module.exports = function(passport) {
    // =========================================================================
    // passport session setup ==================================================
    // =========================================================================
    // required for persistent login sessions
    // passport needs ability to serialize and unserialize users out of session

    // used to serialize the user for the session
    passport.serializeUser(function(user, done) {
            done(null, user.id);
    });

    // used to deserialize the user
    passport.deserializeUser(function(id, done) {
            connection.query("select * from users where id = " + id, function(err, rows) {
                    done(err, rows[0]);
            });
    });


    // =========================================================================
    // LOCAL SIGNUP ============================================================
    // =========================================================================
    // we are using named strategies since we have one for login and one for signup
    // by default, if there was no name, it would just be called 'local'

    passport.use('local-signup', new LocalStrategy({
                    // by default, local strategy uses username and password, we will override with email
                    usernameField: 'email',
                    passwordField: 'password',
                    passReqToCallback: true // allows us to pass back the entire request to the callback
            },
            function(req, email, password, done) {
                    Model.User.findOne({
                            where: {
                                    email: email
                            }
                    }).then(function(user) {
                            if (user == null) {
                                    Model.User.create({
                                            email: email,
                                            password: password
                                    }).then(function(user) {
                                            return done(null, user);
                                    }).catch(function(err) {
                                            return done(null, err);
                                    });
                            } else {
                                    return done(null, false);
                            }
                    })

            }));

};

The Signup api:

router.post('/signup', passport.authenticate('local-signup'), function(req, res) {
                // If this function gets called, authentication was successful.
                // `req.user` contains the authenticated user.
                console.log(req.user);
                if(req.user){
                    res.send({
                        success: true,
                        response: 'signup successful'
                    });
                } else {
                    res.send({
                        success: false,
                        response: 'Email already in use'
                    });
                }
        });

The User model is:

//models/users.js
var Sequelize = require('sequelize')

var attributes = {
                id: {
                        type: Sequelize.INTEGER,
                        primaryKey: true,
                        autoIncrement: true
                },
                name: {
                        type: Sequelize.STRING
                },
                email: {
                        type: Sequelize.STRING
                },
                password: {
                        type: Sequelize.STRING
                },
                created_by: {
                        type: Sequelize.INTEGER
                }
}

var options = {
                // Add the timestamps attributes (updatedAt, createdAt)
                timestamps: true,

                // don't delete database entries but set the newly added attribute deletedAt
                // to the current date (when deletion was done). paranoid will only work if
                // timestamps are enabled
                paranoid: true,

                // don't use camelcase for automatically added attributes but underscore style
                // so updatedAt will be updated_at
                underscored: true,

                // disable the modification of table names; By default, sequelize will automatically
                // transform all passed model names (first parameter of define) into plural.
                // if you don't want that, set the following
                freezeTableName: true,

                // define the table's name
                tableName: 'users'
}

module.exports.attributes = attributes
module.exports.options = options

The automated table creation model script is:

// models/models.js
var UserMeta = require('./users.js'),
        connection = require('./index.js')

var User = connection.define('users', UserMeta.attributes, UserMeta.options)
        // force: true will drop the table if it already exists
User.sync({
        force: true,
        match: /_servernew$/
}).then(function() {
        // Table created
        return User.create();
});
// you can define relationships here
module.exports.User = User;

Upvotes: 1

Views: 2077

Answers (1)

Abhijeet Chakravorty
Abhijeet Chakravorty

Reputation: 227

So i came up with the solution. The following code needs to be changed.

router.post('/signup', function(req, res, next) {
                passport.authenticate('local-signup', function(err, user, info) {
                        if(user){
                                req.logIn(user, function(err) {
                                        if (err) { 
                                                return next(err); 
                                        } else {
                                                res.send({
                                                        success: true,
                                                        response: 'signup successful'
                                                });
                                        }
                                });
                        }

                        if(!user){
                                res.send({
                                        success: false,
                                        response: 'Authentication Failed'
                                });
                        }

                        if(err){
                                res.send({
                                        success: false,
                                        response: 'Authentication failed'
                                })
                        }
                })(req, res, next);
        });

and the passport.js code should be like this.

 // =========================================================================
        // LOCAL SIGNUP ============================================================
        // =========================================================================
        // we are using named strategies since we have one for login and one for signup
        // by default, if there was no name, it would just be called 'local'

        passport.use('local-signup', new LocalStrategy({
                        // by default, local strategy uses username and password, we will override with email
                        usernameField: 'email',
                        passwordField: 'password',
                        passReqToCallback: true // allows us to pass back the entire request to the callback
                },
                function(req, email, password, done) {

                        Model.User.findOne({
                                where: {
                                        email: email
                                }
                        }).then(function(user, err) {
                                console.log('I entered'+user);
                                console.log('I entered'+err);
                                if(err) {
                                        console.log(err);
                                        return done(null, false);
                                }

                                if(user == null) {
                                                Model.User.create({
                                                        email: email,
                                                        password: password
                                                }).then(function(user) {
                                                        return done(null, user);
                                                }).catch(function(err) {
                                                        return done(null, err);
                                                });
                                }

                                if(user){
                                        return done(null, false);
                                }

                        })

                }));

It will work just like a charm :D.

Upvotes: 3

Related Questions