Search code examples
javascriptnode.jsmongodbpassport.jspassport-local

PassportJS not passing signup data to database, possible database connection issue


I'm using a scotch.io tutorial on setting up PassportJS local login for an app I'm working on. After setting up my signup/login pages, MongoDB, PassportJS, I tried making a test "account", and.... nothing happens. No redirect, no flash message, nothing. It seems like nothing is getting picked up to post to the database. I'm including the pertinent scripts below. I tried to be as faithful as possible to the tutorial layout (source: https://scotch.io/tutorials/easy-node-authentication-setup-and-local).

index.js (entry point for server)

// load dependencies
var express = require('express');
var app = express();
var path = require('path');

var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var LocalStrategy = require('passport-local').Strategy;

var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var session = require('express-session');

var configDB = require('./config/database.js');

// setup views
var ejs = require('ejs');
var engine = require('ejs-locals');

//database configuration
mongoose.connect(configDB.url);

require('./config/passport')(passport);

app.use(morgan('dev'));
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

//view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use('/public', express.static(path.join(__dirname, 'public')));

// required for passport
app.use(session({ 
    secret: 'ilovescotchscotchyscotchscotch',
    resave: true,
    saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // for flash messages stored in-session

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

app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
});

passport.js (passport settings)

var LocalStrategy = require('passport-local').Strategy;

var User = require('../user');

module.exports = function(passport) {
    passport.serializeUser(function(user, done) {
        done(null, user.id);
    });

    passport.deserializeUser(function(id, done) {
        User.findById(id, function(err, user) {
            done(err, user);
        });
    });

    passport.use('local-signup', new LocalStrategy({
        usernameField: 'email',
        passwordField: 'password',
        passReqToCallback: true
    },
    function(req, email, password, done) {
        process.nextTick(function() {
            User.findOne({ 'local.email': email }, function(err, user) {
                if (err)
                    return done(err);
                if (user) {
                    return done(null, false, req.flash('signupMessage', 'That e-mail address is already taken.'));
                } else {
                    var newUser = new User;
                    newUser.local.email = email;
                    newUser.local.password = newUser.generateHash(password);
                    newUser.save(function(err) {
                        if (err)
                            throw err;
                        return done(null, newUser);
                    });
                }
            });
        });
    }));

    passport.use('local-login', new LocalStrategy({
        usernameField: 'email',
        passwordField: 'password',
        passReqToCallback: true
    },
    function(req, email, password, done) {
        User.findOne({ 'local.email': email }, function(err, user) {
            if (err)
                return done(err);
            if (!user)
                return done(null, false, req.flash('loginMessage', 'User not found.'));
            if(!user.validPassword(password))
                return done(null, false, req.flash('loginMessage', 'Invalid password, please try again.'));
            return done(null, user);
        });
    }));
};

/config/database.js

module.exports = {
    'url': 'mongodb://localhost:27017/users/'
}

routes.js

module.exports = function(app, passport) {
    app.get('/', function (req, res) {
        res.render('pages/index');
    });

    app.get('/login', function (req, res) {
        res.render('pages/login', { message: req.flash('loginMessage') });
    });

    app.get('/signup', function (req, res) {
        res.render('pages/signup', { message: req.flash('signupMessage') });
    });

    app.post('/signup', passport.authenticate('local-signup', {
        successRedirect: '/profile',
        failureRedirect: '/signup',
        failureFlash: true
    }));

    app.post('/login', passport.authenticate('local-login', {
        successRedirect: '/profile',
        failureRedirect: '/login',
        failureFlash: true
    }));

    app.get('/profile', isLoggedIn, function(req, res) {
        res.render('profile.ejs', {
            user: req.user
        });
    });

    app.get('/addroom', function (req, res) {
        res.render('pages/addroom.ejs', {
            message: req.flash('addroomMessage')
        });
    });

    app.get('/propconfig', function (req, res) {
        res.render('pages/propconfig.ejs', {
            message: req.flash('propconfigMessage')
        });
    });
    app.get('/logout', function(req, res) {
        req.logout();
        res.redirect('/');
    });
};

// route middleware to ensure a user is logged in
function isLoggedIn(req, res, next) {
    if (req.isAuthenticated())
        return next();
    res.redirect('/');
}

user.js (user schema)

// load dependencies
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');

// define user schema
var userSchema = mongoose.Schema({
    local: {
        email: String,
        password: String
    }
});

// METHODS
// generating a hash

userSchema.methods.generateHash = function(password) {
    return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};

// checking if password is valid
userSchema.methods.validPassword = function(password) {
    return bcrypt.compareSync(password, this.local.password);
};

// creating model for user to expose to app
module.exports = mongoose.model('User', userSchema);

and finally, signup.ejs (template for signup UI)

<!doctype html>
<html>
<head>
    <title>Node Authentication</title>
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"> <!-- load bootstrap css -->
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
    <style>
        body        { padding-top:80px; }
    </style>
    <script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
    <script   src="http://code.jquery.com/ui/1.12.1/jquery-ui.min.js"   integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="   crossorigin="anonymous"></script>
</head>
<body>
<div class="container">

<div class="col-sm-6 col-sm-offset-3">

    <h1><span class="fa fa-sign-in"></span> Signup</h1>

    <!-- show any messages that come back with authentication -->
    <% if (message.length > 0) { %>
        <div class="alert alert-danger"><%= message %></div>
    <% } %>

    <!-- LOGIN FORM -->
    <form action="/signup" method="post">
        <div class="form-group">
            <label>Email</label>
            <input type="text" class="form-control" name="email" id="email">
            <div id="emailError"></div>
        </div>
        <div class="form-group">
            <label>Password</label>
            <input type="password" class="form-control" name="password" id="password">
            <div id="passwordError"></div>
        </div>

        <button type="submit" class="btn btn-warning btn-lg">Signup</button>
    </form>

    <hr>

    <p>Already have an account? <a href="/login">Login</a></p>
    <p>Or go <a href="/">home</a>.</p>

</div>

</div>
<script type="text/javascript" src="../public/javascripts/validate.js"></script>
</body>
</html>

Update: I commented out the email validation script I was using, and now the POST action is happening (why it wasn't before, I have no idea), but it's still not getting to the database.

Update #2: I tried running the server in debug mode. The console returned this information:

GET /signup 304 34.791 ms - -
  express:router dispatching POST /signup +28s
  express:router query  : /signup +0ms
  express:router expressInit  : /signup +0ms
  express:router logger  : /signup +0ms
  express:router cookieParser  : /signup +1ms
  express:router jsonParser  : /signup +0ms
  express:router urlencodedParser  : /signup +3ms
  express:router session  : /signup +157ms
  express:router initialize  : /signup +2ms
  express:router authenticate  : /signup +0ms
  express:router <anonymous>  : /signup +1ms
POST /signup - - ms - -
  express:router dispatching POST /signup +2m
  express:router query  : /signup +0ms
  express:router expressInit  : /signup +0ms
  express:router logger  : /signup +1ms
  express:router cookieParser  : /signup +1ms
  express:router jsonParser  : /signup +2ms
  express:router urlencodedParser  : /signup +0ms
  express:router session  : /signup +4ms
  express:router initialize  : /signup +1ms
  express:router authenticate  : /signup +2ms
  express:router <anonymous>  : /signup +1ms
POST /signup - - ms - -

Meanwhile, the browser stayed on "Waiting for localhost..." for about 2 or 3 minutes (without crashing or timing out), before finally returning "The localhost page isn’t working

localhost didn’t send any data. ERR_EMPTY_RESPONSE"

Update #3: This is another debug log, checking all modules (not just Express):

  express:router dispatching POST /signup +39s
  express:router query  : /signup +4ms
  express:router expressInit  : /signup +1ms
  express:router logger  : /signup +2ms
  express:router cookieParser  : /signup +5ms
  express:router jsonParser  : /signup +3ms
  body-parser:json content-type "application/x-www-form-urlencoded" +0ms
  body-parser:json skip parsing +4ms
  express:router urlencodedParser  : /signup +1ms
  body-parser:urlencoded content-type "application/x-www-form-urlencoded" +0ms
  body-parser:urlencoded content-encoding "identity" +3ms
  body-parser:urlencoded read body +0ms
  body-parser:urlencoded parse body +60ms
  body-parser:urlencoded parse extended urlencoding +5ms
  express:router session  : /signup +8ms
  express-session fetching 6M-W_9dEFDmsPBvNp2d0UmeBE9gDuXMn +14ms
  express-session no session found +3ms
  express:router initialize  : /signup +9ms
  express:router authenticate  : /signup +0ms
  express:router <anonymous>  : /signup +2ms
req.body: {"email":"matt@matt.matt","password":"pass1234"}
  mquery findOne +20ms users { 'local.email': 'matt@matt.matt' } { fields: {} }
  express-session saving EkaL4tXbUCZ9QXHDGt2XWxlgMjVGejAC +95ms
  express-session split response +1ms
  express-session set-cookie connect.sid=s%3AEkaL4tXbUCZ9QXHDGt2XWxlgMjVGejAC.cvTE1KsUvIzbnNqHP0ns9td75MUkR4mKRDvwuHk%2B4jE; Path=/; HttpOnly +4ms
  morgan log request +17ms
POST /signup 302 237.147 ms - 58
  express:router dispatching GET /signup +9ms
  express:router query  : /signup +2ms
  express:router expressInit  : /signup +0ms
  express:router logger  : /signup +1ms
  express:router cookieParser  : /signup +2ms
  express:router jsonParser  : /signup +0ms
  body-parser:json skip empty body +0ms
  express:router urlencodedParser  : /signup +13ms
  body-parser:urlencoded skip empty body +0ms
  express:router session  : /signup +1ms
  express-session fetching EkaL4tXbUCZ9QXHDGt2XWxlgMjVGejAC +1ms
  express-session session found +3ms
  express:router initialize  : /signup +1ms
  express:router authenticate  : /signup +1ms
  express:router <anonymous>  : /signup +1ms
  express:view lookup "pages/signup.ejs" +6ms
  express:view stat "/media/matt/PORTABLE/myapp/views/pages/signup.ejs" +1ms
  express:view render "/media/matt/PORTABLE/myapp/views/pages/signup.ejs" +1ms
  express-session saving EkaL4tXbUCZ9QXHDGt2XWxlgMjVGejAC +18ms
  express-session split response +0ms
  morgan log request +10ms
GET /signup 200 50.407 ms - 1736

Solution

  • Thanks for your code. I think it helped me fix this faster. I was able to reproduce your issue. Here is a diff of what I did to fix your issue

    Change #1

    In routes.js, I modified passport to use local-signup defined in passport.js instead of your existing function:

    diff --git a/routes.js b/routes.js
    index a6166b7..e2a742f 100644
    --- a/routes.js
    +++ b/routes.js
    @@ -13,30 +13,12 @@ module.exports = function(app, passport) {
                    res.render('pages/signup', { message: req.flash('signupMessage') });
            });
    
    -       app.post('/signup', function (req, res, next) {
    -    passport.authenticate('local', {failureRedirect: '/login'},
    -        function (req, email, password, done) {
    -            process.nextTick(function () {
    -                User.findOne({'local.email': email}, function (err, user) {
    -                    if (err)
    -                        return done(err);
    -                    if (user) {
    -                        return done(null, false, req.flash('signupMessage', 'That e-mail address is already taken.'));
    -                    } else {
    -                        var newUser = new User;
    -                        newUser.local.email = email;
    -                        newUser.local.password = newUser.generateHash(password);
    -                        newUser.save(function (err) {
    -                            if (err){
    -                                throw err;}
    -                            //res.redirect("/path")        
    -                            return done(null, newUser);
    -                        });
    -                    }
    -                });
    -            });
    -        });
    -});
    +           // process the signup form
    +    app.post('/signup', passport.authenticate('local-signup', {
    +        successRedirect : '/profile', // redirect to the secure profile section
    +        failureRedirect : '/signup', // redirect back to the signup page if there is an error
    +        failureFlash : true // allow flash messages
    +    }));
    
            app.post('/login', passport.authenticate('local-login', {
                    successRedirect: '/profile',
    

    Change #2

    Commented out few comments and made sure if err is closed

    diff --git a/config/passport.js b/config/passport.js
    index dde7628..398796e 100644
    --- a/config/passport.js
    +++ b/config/passport.js
    @@ -23,7 +23,6 @@ module.exports = function(passport) {
                    process.nextTick(function() {
                            User.findOne({ 'local.email': email }, function(err, user) {
                                    if (err)
    -                                       console.log(err);
                                            return done(err);
                                    if (user) {
                                            return done(null, false, req.flash('signupMessage', 'That e-mail address is already taken.'));
    @@ -31,9 +30,9 @@ module.exports = function(passport) {
                                            var newUser = new User;
                                            newUser.local.email = email;
                                            newUser.local.password = newUser.generateHash(password);
    -                                       newUser.$__save(function(err) {
    +
    +                                       newUser.save(function(err) {
                                                    if (err)
    -                                                       console.log(err);
                                                            throw err;
                                                    console.log(User);
                                                    console.log(newUser);
    

    Here is a gif to show it is successful: enter image description here

    I have submitted a pull request to your branch.