Passport login error

I started learning how to use passport today, I’m not quite sure why login fails…

here is the code…

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const hbs = require('hbs');
const morgan = require('morgan');
const session = require('express-session');
const cookieParser = require('cookie-parser');
const MongoStore = require('connect-mongo')(session);
const passport = require('passport');
const passportConfig = require('./passport');
const User = require('./models/user');
const app = express();


const url = '<mongoURI>';

mongoose.connect(url, (err) => {
  if (err) {
    console.log(err);
  } else {
    console.log('Connected to mongoose...');
  }
});

app.set('view engine', 'hbs');
app.use(morgan('dev'));
app.use(cookieParser());
app.use(bodyParser.json());
app.use(express.static(__dirname + '/public'));
hbs.registerPartials(__dirname + '/views/partials');
app.use(bodyParser.urlencoded({
  extended: true
}));
app.use(session({
  secret: 'hello',
  resave: false,
  saveUninitialized: true,
  store: new MongoStore({
    url,
    autoReconnect: true
  })
}));


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

// home route
app.get('/', (req, res) => {
  res.render('home');
});

// login route
app.get('/login', (req, res) => {
  if (req.user) return res.redirect('/');
  res.render('login');
});
app.post('/login',
  passport.authenticate('local', {
    successRedirect: '/profile',
    failureRedirect: '/login'
  })
);

// signup route

app.get('/profile', (req, res) => {
  res.render('profile');
});

// logout
app.get('/logout', (req, res) => {
  req.logout();
  res.redirect('/login');
});

// create user
app.post('/posts', (req, res) => {
  let user = User();
  user.username = req.body.username;
  user.password = req.body.password;
  user.save((err) => {
    if (err) res.json('Error!!!')
    res.json(user);
  });
});

// start server
app.listen(3000, (err) => {
  if (err) {
    console.log('Unable to connect', err);
  } else {
    console.log('Server running...');
  }
});


And the code for passport…

const passport = require('passport');
const User = require('./models/user');
const LocalStrategy = require('passport-local').Strategy;


// store the user._id in the session
passport.serializeUser((user, done) => {
  done(null, user._id);
});
// fetch the user._id from database
passport.deserializeUser((id, done) => {
  User.findById(id, (err, user) => {
    done(err, user);
  });
});
// sign in //local-login
passport.use(new LocalStrategy(
  (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);
    });
  }
));

What is the error that you’re getting?

Right off the bat, I notice this line. Are you substituting in the URI for mongo?

The failureRedirect code runs every time, i dont know why…

from this block

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

it is supposed to redirect to ‘/profile’ if successful, however it keeps, redirecting to ‘login’, meaning something went wrong and i cant find it

i intenionally put that there as a placeholder, the original value is a mongolab url…

1 Like

Sorry, but I’ve got to run to a gig. With a quick scan I can’t see anything strange. But I gotta go now. I might suggest uploading it to github so we can see the actual code and file structure.

here’s the repo link

https://github.com/charlesonunze/nodejs2/tree/master/udemyclone

OK, first let me start off with the caveat that I am learning too and am in no way and expert on passport. I had to hack my way through these too.

I finally got a chance to look at this. I’ve got it up and running. My question is: How do I register as a new user?

So I run it and it I get to the authentication and it tries to authenticate but there are no users in my database. In your passport.js file, I put the following console.log statements in the passport.use(…:

passport.use(new LocalStrategy(
  (username, password, done) => {
    console.log('*** in passport.use')
    User.findOne({
      username: username
    }, function(err, user) {
      console.log('*** in User.findOne callback')
      if (err) {
        console.log('*** err', err)
        return done(err);
      }
      if (!user) {
        console.log('*** no username')
        return done(null, false, {
          message: 'Incorrect username.'
        });
      }
      if (!user.validPassword(password)) {
        console.log('*** bad password')
        return done(null, false, {
          message: 'Incorrect password.'
        });
      }
      console.log('*** found?')
      return done(null, user);
    });
  }
));

When I try to login, I get the follow output in the terminal:

Server running on port 3000...
Connected to mongoose...
GET /login 304 19.072 ms - -
GET /css/font-awesome.css 304 3.839 ms - -
GET /css/style.css 304 2.532 ms - -
GET /images/banner.jpg 304 0.542 ms - -
*** in passport.use
*** in User.findOne callback
*** no username
POST /login 302 118.354 ms - 56
GET /login 200 3.050 ms - 2496
GET /css/font-awesome.css 304 2.170 ms - -
GET /css/style.css 304 2.423 ms - -
GET /images/banner.jpg 304 0.509 ms - -

So, it’s trying to login correctly, but there is no username in the database so it is (correctly) rejecting it. So, this seems to work exactly as it should – if there’s no user in the database, no one should be able to login.

Did you manually insert a user into the database? That might be awkward to do because we typically use some kind of a hash for the password.

You need a route to create a new user, usually something like /register. These are the routes that I used:

router.get('/register', function(req, res) {
  res.render('auth/register');
});

router.post('/register', function(req, res) {

  // omitted code to trim and validate

  User.count({ 'username': req.body.username }, function (err, count) { // check if user exists
    if (err) throw err;

    req.getValidationResult().then(function(result) {

      var errors = result.array();

      if (count>0) {
        errors.push( { param: 'username', msg: 'That username is already in use.', value: 'u'} );
      }
      if (errors.length) {
        res.render('auth/register', {
          errors: errors
        });
      } else {
        var newUser = new User({
          name: req.body.name,
          username: req.body.username,
          password: req.body.password,
          city: req.body.city,
          state: req.body.state,
          country: req.body.country,
          email: req.body.email
        });
        User.createUser(newUser, function(err, user) {  // create new user
          if (err) throw err;
          req.flash("success_msg", "You are registered and can now log in.");
          res.redirect("/users/login");
        });
      }
    });
  });
});

You may have to make some changes to make this work for you, but this is the idea. You GET a route to have them fill in the forms and then submit with a POST that checks and creates the user.

You’re also going to want to hash the password. (If you don’t understand what that means, you may want to look that up.) So you will need a ways to create the User in your mongoose model in models/user.js, something like:

var mongoose = require("mongoose");
var bcrypt = require("bcryptjs");

 // …

module.exports.createUser = function(newUser, callback) {
  bcrypt.genSalt(10, function(err, salt) {
    bcrypt.hash(newUser.password, salt, function(err, hash) {
        newUser.password = hash;
        newUser.save(callback);
    });
  });
}

That will create a user in the database that can then be checked on login.

I think those are the things you want to work on first.

I might add a few other things.

You should look into a package called dotenv. It will allow you to put some of your system variables into a “hidden” file. These should be variables that you either don’t want anyone to see (passwords, API keys, your session “secret”, etc.) or things that may change and you can change them in one location (database urls, ports). The latter was very convenient to me for when I ported to heroku because those were the things I needed to assign in the environment variables anyway. An important point is that you do not save that file (.env) to github, it must be included in your .gitignore. That will keep secret info secret.

Another thing is the structure of the routes. It’s very common to have a folder called “routes” where you put various route files. For example, the routes that you currently have in your app.js, I would move into a file called routes/auth.js.

That file might look like:

var express = require('express');
var passport = require('passport');
var router = express.Router();



// register

router.get('/register', function(req, res) {
  res.render('<register hbs file>');
});

 // … etc, etc

module.exports = router;

And then somewhere in your app.js you would have something like:

var users = require('./routes/users');

// …

app.use('/users', users);

An important difference here is that instead of localhost:3000/register, now it’s localhost:3000/auth/register. But this allows you to organize your routes a little better – both in the file structure and in the url.

So, there are three things here – the register problem, the dotenv thing, and the routes. I wouldn’t suggest tackling them all at once. Just take them one at a time.

I hope this helps. Like I said, I’m still kind of figuring this out too, I’m just a step and a half ahead of you.

Let us know if you have more questions.

2 Likes

Not directly related to the OP’s question but I thought I would mention that the flow chart in the accepted answer of this SO post really helpful.

The other thing that I found helpful is to putting console.log() statements inside passport.serializeUser() and passport.deserializeUser() and try logging in, registering, and logging out (I won’t spoil it for you).

1 Like

this helped me out. i created a user model and a signup route for adding new users to the db.

plus, passwords are now being hashed.

everything works fine now, thanks

This helped me because I had left out a return of the callback after authentication.