2

edit: sorry it's so long now, added in the compiled JS! I also added Kevs suggestions.

I am developing a MEAN stack application (MongoDB, ExpressJS, Angular, NodeJS) and having a good time with it, especially with coffee script. My issue is that I can't get passport.js to work, every time it returns {success: false}. When I do a console.log in my LocalStrategy function it seems it's never even being called. Any idea why?

Some snippets

server.coffee (main file)

...
Account = mongoose.model 'account'

passport.use 'local-login', new LocalStrategy (_username, password, done) ->
  Account.findOne {username:_username}, (error, user) ->
    if error
      done error
    if not user
      done null, false, {message:'Incorrect username.'}
    done null,user
passport.serializeUser (user, done) ->
  console.log 'serialize user'
  if user
    done null, user
passport.deserializeUser (id, done) ->
  Account.findOne({_id:id}).exec (error, user) ->
    if user
      return done null, user
    else
      return done null, false
...

compiled js

...
Account = mongoose.model('account');

  passport.use('local-login', new LocalStrategy(function(_username, password, done) {
    return Account.findOne({
      username: _username
    }, function(error, user) {
      if (error) {
        done(error);
      }
      if (!user) {
        done(null, false, {
          message: 'Incorrect username.'
        });
      }
      return done(null, user);
    });
  }));

  passport.serializeUser(function(user, done) {
    console.log('serialize user');
    if (user) {
      return done(null, user);
    }
  });

  passport.deserializeUser(function(id, done) {
    return Account.findOne({
      _id: id
    }).exec(function(error, user) {
      if (user) {
        return done(null, user);
      } else {
        return done(null, false);
      }
    });
  });
...

routes.coffee

...
  app.post '/login', (request, result, next) ->
    auth = passport.authenticate 'local-login', (err, user) ->
      if err
        next err
      if not user
        result.send {success:false}
      request.logIn user, (err) ->
        if err
          next err
        result.send {success:true}
    auth request, result, next
...

compiled js

...
    app.post('/login', function(request, result, next) {
      var auth;
      auth = passport.authenticate('local-login', function(err, user, info) {
        if (err) {
          next(err);
        }
        if (!user) {
          result.send({
            success: false
          });
        }
        return request.logIn(user, function(err) {
          if (err) {
            next(err);
          }
          return result.send({
            success: true
          });
        });
      });
      return auth(request, result, next);
    });
...

mongo.coffee

...
  accountSchema = mongoose.Schema {
    username: String
    firstname: String
    lastname: String
    email: String
  }

  Account = mongoose.model 'account', accountSchema

  Account.find({}).exec (error, collection) ->
    if collection.length == 0
      Account.create {
        username: 'alex'
        firstname: 'Alex'
        lastname: 'Hxxx'
        email: 'axxxxx@gmail.com'
      }
...

compiled js

  module.exports = function(config) {
    var Account, accountSchema, db;
    mongoose.connect("mongodb://" + config.db_host + "/" + config.db_name);
    db = mongoose.connection;
    db.on('error', function() {
      return console.error('Database connection error');
    });
    db.once('open', function() {
      return console.log('Database connection established');
    });
    accountSchema = mongoose.Schema({
      username: String,
      firstname: String,
      lastname: String,
      email: String
    });
    Account = mongoose.model('account', accountSchema);
    return Account.find({}).exec(function(error, collection) {
      if (collection.length === 0) {
        return Account.create({
          username: 'alex',
          firstname: 'Alex',
          lastname: 'Hxxx',
          email: 'axxxxxxxxx@gmail.com'
        });
      }
    });
  };

express.coffee

...
  app.use cookie-parser(config.cookiesecret)
  app.use session {secret: config.sessionsecret}
  app.use passport.initialize()
  app.use passport.session()
  app.use bodyParser()
...

compiled js

...
  app.use(cookie-parser(config.cookiesecret));
  app.use(session({
    secret: config.sessionsecret
  }));
  app.use(passport.initialize());
  app.use(passport.session());
  app.use(bodyParser());
...

my angular login controller

app.controller 'loginCtrl', ['$scope', '$http', ($scope, $http) ->
  $scope.login = (username, password) -> 
    $http.post('/login', {username: username, password: password}).then (response) ->
      console.log response.data
      if response.data.success
        console.log "Logged in as #{username}"
      else
        console.log "Failed to login as #{username}"
]

compiled js

  app.controller('loginCtrl', [
    '$scope', '$http', function($scope, $http) {
      return $scope.login = function(username, password) {
        return $http.post('/login', {
          username: username,
          password: password
        }).then(function(response) {
          console.log(response.data);
          if (response.data.success) {
            return console.log("Logged in as " + username);
          } else {
            return console.log("Failed to login as " + username);
          }
        });
      };
    }
  ]);

and my login form (jade):

h3 Login
  form(ng-controller="loginCtrl")
    .row
      .columns.small-12.large-12
        form
          label Username
          input(placeholder="username",name="username",type="text",ng-model="username")
          label Password
          input(placeholder="password",name="password",type="password",no-model="password")
          div(align="center")
            button.button(ng-click="login(username,password)") Login
            span  
            a.button(href="/user/Register") Register

The sample user is being created fine so mongoose is okay and is all set up before I start working with passport. I've followed a few different tutorials and it's driving me crazy!

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Alex Howe
  • 103
  • 1
  • 1
  • 8
  • initial debugging thoughts... in your Account collection is _id an ObjectId? and if so when you do: Account.findOne({_id:id}) is the 'id' you are querying on also an ObjectId? otherwise it will always return no results. You may have to wrap it in db.ObjectId(id) – Kev Price May 09 '14 at 16:21
  • @kev it seems never to get past console.log 'serialize user' if I put a console.log after Account.findOne {username:_username} it never reaches the console, so I am not sure _id ever gets used. – Alex Howe May 09 '14 at 16:22
  • ok- I'm looking through my own passport strategies and the difference I can see between what you are doing and what I have done is I have named my strategy - eg: passport.use('local-login', new LocalStrategy({ I then authenticate using: passport.authenticate('local-login',function(err,user,info){ so in your case you should just use the first parameter of passport.use as 'local' and it should hopefully work – Kev Price May 09 '14 at 16:36
  • I have added that an. Would it help if I added in the compiled JavaScript? You might want to add a top level comment too so I can up vote (or whatever it's called here)! – Alex Howe May 09 '14 at 16:44
  • yeah sorry I'm not writing my answer in coffee script (I have no experience with it so don't want to get the syntax wrong and confuse things further) - I hope that works for you. I've added a top level comment – Kev Price May 09 '14 at 16:54
  • It's okay, as long as you can read it. I am backwards compatible ;) lol – Alex Howe May 09 '14 at 16:59
  • Did you get Express to use Passport? app.use(passport.initialize()); app.use(passport.session()); – Deniz Ozger May 09 '14 at 18:09
  • @DenizOzger Sorry I forgot to add that, yes I have both. also express-session and cookie-parser. – Alex Howe May 09 '14 at 18:12
  • Tried just `Account.findOne` (without returning) instead of `return Account.findOne`? – laggingreflex May 10 '14 at 02:07
  • @laggingreflex Shouldn't make a difference but cofffeescript always returns's the last statement. – Alex Howe May 10 '14 at 07:05
  • Try plugging [this custom callback](http://pastebin.com/aP1NW8WA) (more [info](http://passportjs.org/guide/authenticate/)) and see what the output says. – laggingreflex May 10 '14 at 14:13
  • @laggingreflex error is undefined, user is always false. Info gives me {message:"Missing credentials"}. I had a feeling it may not have been passing the username/password but I couldn't find what I am missing in my example. Do I need to pass it the credentials somewhere? – Alex Howe May 10 '14 at 14:23
  • In your login jade, there's no `action="/login" method="post" ..` attribute attached to your form. – laggingreflex May 10 '14 at 14:30
  • Also, there's no `` either, so the form isn't ever actually submitted. – laggingreflex May 10 '14 at 14:33
  • I see you're using Angular's `ng-click` to handle. I'm not that proficient with angular, but are you doing the form submission in that angular method correctly? It doesn't actually have to be the form itself that submits itself, you can do that in angular too I guess, but obviously it's not being done right because there's that "missing creds" error. – laggingreflex May 10 '14 at 14:41
  • @laggingreflex I can't get it to execute the function at passport.use 'local-login'y period. I am not sure why. Nothing I console.log there gets put out when I attempt a login. The username is being POSTed fine though, I already found it by console.log'ing req.data.username. I think what's happening is my function is never being called... I am just not sure where to do it. – Alex Howe May 10 '14 at 15:47
  • @laggingreflex it is POSTed, I don't need a conventional form. On ng-click I call $http.post. It's being POSTed, I can see the data that's being POSTed in my console. – Alex Howe May 10 '14 at 15:50
  • It needs to be `req.body.username`, not `req.data`. – laggingreflex May 10 '14 at 15:55
  • @laggingreflex that's what I meant, I had logged that to the console before. I don't know where to pass req.body.username. Where in my code does that go? – Alex Howe May 10 '14 at 15:56
  • Can you see the `req.body.username` from the server-side? http://pastebin.com/jZe4MAn6 – laggingreflex May 10 '14 at 16:02
  • @laggingreflex yep, that's what I meant. Just tested again and I get the right username logged right before the missing credentials (on the server side). Very frustrating. I appreciate your time so far by the way very much. – Alex Howe May 10 '14 at 16:09
  • Move the `app.use(bodyParser())` above the `passport.initialize` & `passport.session`!!! – laggingreflex May 11 '14 at 00:47
  • @laggingreflex Still missing credentials somewhere – Alex Howe May 11 '14 at 10:56
  • Are you running this on Windows by chance? I am having similar mysterious PassportJS problems on Windows when the same app works fine on Linux. – blah238 May 12 '14 at 21:11

3 Answers3

3

You're not setting the initial parameter of passport.use and passing the req back to the callback for example - from my code:

passport.use('local-login', 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) {

    // asynchronous
    // User.findOne wont fire unless data is sent back
    process.nextTick(function() {

      db.Account.findOne( { ...

You can then authenticate using:

passport.authenticate('local-login',function(err,user,info){

In your case you are calling passport.authenticate 'local' so your first parameter in passport.use should be 'local' so that it calls the correct strategy

Kev Price
  • 799
  • 9
  • 21
  • yup, changed it to passport.use 'local-login', new LocalStrategy (_username, password, done) -> and auth = passport.authenticate 'local-login', (err, user, info) -> (I was previously missing 'info' too.) the main tutorial I am using didn't have a name for passport.use either - I am guessing local is the default? Maybe this code is cursed! Still doesn't work. Thanks for your time though. – Alex Howe May 09 '14 at 16:57
  • 1
    I also added in the compiled JS if that helps. – Alex Howe May 09 '14 at 17:14
  • Changed my code to "passport.use 'local-login', new LocalStrategy {usernameField : 'username', passReqToCallback : true}, (request, _username, password, done) ->" (which doesn't seem to be in any of the other examples but I'll roll with it if it works), still nothing. I don't think passport likes me... – Alex Howe May 09 '14 at 17:52
0

My passport strategies are almost identical, with one exception: serializing user to its _id property. I suspect that might be the problem, since in your deserialize method you are using the _id directly, but passing done null, user in serialize.

passport.serializeUser (user, done) ->
  if user
    done null, user?._id
Corkscreewe
  • 2,921
  • 2
  • 22
  • 23
  • Still getting info{"message":"Missing credentials"} back in info. Anything I try to console.log in that function never shows up so I am assuming it never gets called in the first place for some reason. – Alex Howe May 10 '14 at 22:30
0

I think its even simpler than the answers posted here. you need if error? and if not user? with the ?s. Without the ?s, it does a true/false check, but with the ?s it does a null check which is what you want.

Try this code:

Account = mongoose.model 'account'

passport.use 'local-login', new LocalStrategy (_username, password, done) ->
    Account.findOne {username:_username}, (error, user) ->
        if error?
            done error,null
        else if not user?
           done null, false, {message:'Incorrect username.'}
        else
           done null,user
passport.serializeUser (user, done) ->
   console.log 'serialize user'
   if user?
       done null, user._id
passport.deserializeUser (id, done) ->
    Account.findOne({_id:id}).exec (error, user) ->
        if user?
            return done null, user
        else
            return done null, false
TJC
  • 717
  • 3
  • 12