54

I'm sure I'm missing something really obvious here, but I can't figure this out. The function I've passed to the LocalStrategy constructor doesn't get called when the login form gets submitted.

Code:

var express = require('express');
var http = require('http');
var path = require('path');
var swig = require('swig');
var passport = require('passport');

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

passport.serializeUser(function(user, done) {
  console.log('Serialize user called.');
  done(null, user.name);
});

passport.deserializeUser(function(id, done) {
  console.log('Deserialize user called.');
  return done(null, {name: 'Oliver'});
});

passport.use(new LocalStrategy(
  function(username, password, done) {
    console.log('local strategy called with: %s', username);
    return done(null, {name: username});
  }));

var app = express();

app.set('port', process.env.PORT || 3000);
app.set('view engine', 'swig');
app.set('views', __dirname + '/views');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('asljASDR2084^^!'));
app.use(express.session());
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(require('less-middleware')({ src: __dirname + '/public' }));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.errorHandler({ dumpExceptions:true, showStack:true }));

app.engine('swig', swig.renderFile);

app.post('/auth', passport.authenticate('local'));
app.get('/login', function(req, res) {
  // login is a simple form that posts username and password /auth
  res.render('login', {});
});

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

The form on my /login page is cut and pasted from the passport docs except that I changed it to submit to /auth instead of /login:

<form action="/auth" method="post">
    <div>
        <label>Username:</label>
        <input type="text" name="username"/>
    </div>
    <div>
        <label>Password:</label>
        <input type="password" name="password"/>
    </div>
    <div>
        <input type="submit" value="Log In"/>
    </div>
</form>

When I sumit the login form, the following gets logged

GET /login 200 5ms - 432b
POST /auth 401 6ms

Note that "local strategy called" is never logged.

Why isn't the function I passed to LocalStrategy getting called?

laggingreflex
  • 32,948
  • 35
  • 141
  • 196
Oliver Dain
  • 9,617
  • 3
  • 35
  • 48

15 Answers15

92

I recently came across this problem and it can be caused by a number of things. First thing to check is ensuring that the bodyParser is set up for express, which I see that you have done.

app.use(express.bodyParser());

The next thing is ensuring that the form you are submitting to the route contains both a username AND password, and the user must also enter something in both fields. I was stumped for a bit testing it and not actually putting anything in the password field while testing :) Passport requires BOTH to execute the LocalStrategy verification function passed to passport.authenticate('local').

Your example also seems to be set up to capture both username and password properly, but in any case, you should try testing that the body is being parsed properly even without passport:

app.post('/auth', function(req, res){
  console.log("body parsing", req.body);
  //should be something like: {username: YOURUSERNAME, password: YOURPASSWORD}
});

Else

Did you try adding a request handler to your /auth route?

app.post('/auth', passport.authenticate('local'), function(req, res){
  console.log("passport user", req.user);
});

or

app.post('/auth', passport.authenticate('local', { successRedirect: '/', failureRedirect: '/auth' }));
Arjun Mehta
  • 2,500
  • 1
  • 24
  • 40
  • I no longer have this code lying around to test so I'm not sure, but I think my code above is doing all the things you suggest. My /auth route is a tiny bit different than yours, as my local strategy takes care of returning a result while yours calls the local strategy as middleware and then has a 2nd function get called to return the result, but I don't think that should matter. If I ever try to use Passport again, I'll look at this post again. – Oliver Dain Nov 30 '13 at 17:33
  • 1
    Sure! I think I struggled with getting it working at first because I didn't quite understand it, but it's quite a useful library once it's up and running. – Arjun Mehta Dec 02 '13 at 17:11
  • 7
    I just ran into the BOTH fields required problem, thank you so much for pointing that out, I've just lost an hour to this and it could have been much longer. – TRayburn May 08 '14 at 04:38
  • Thanks! After hours of scratching my head of it not working. Adding bodyParser worked for me. – Levi Roberts Jul 25 '14 at 16:21
  • @)TRayburn, @)arjun ditto! – bitcruncher Sep 03 '14 at 06:11
  • 5
    If you are using the latest version bodyParser() is deprecated. I used bodyParser.urlencoded({extended:true}) and it worked just fine. – payne8 Feb 27 '15 at 07:06
  • @Arjun Yep, I needed both a username and password (specifically called 'username' and 'password' in my form). Thanks! – Scott Nov 21 '18 at 04:52
  • passportRoute.post('/login/password', (q,s,n)=>{ console.log('here we are'); console.log(q.body); passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' })(q,s,n) } ); passport.authentivate(lskd;ld) returns a function, so need to call it (req, res, next). good tip, – nerkn Feb 08 '22 at 11:42
46

I encountered the same problem when I set up my own route handler from which I called passport.authenticate (). I forgot that passport.authenticate returns middleware that must be invoked, so just calling passport.authenticate isn't sufficient. Then I replaced

router.post("/",
 function(req,res,next){
   passport.authenticate("local", function(err, user, info){

    // handle succes or failure

  }); 
})

with

router.post("/",
 function(req,res,next){
   passport.authenticate("local", function(err, user, info){

    // handle succes or failure

  })(req,res,next); 
})
Jakub Kutrzeba
  • 983
  • 8
  • 12
  • 7
    Thank you for making me realize passport.authenticate() returns a function! – jamesjansson May 01 '18 at 11:18
  • Thank you, i try your code. It's work, but i can not find req.body or req.id. My method router is get (router.get('/',...) – Wicak Jan 17 '19 at 06:18
  • Great, I copied code from sample but missed this small detail (although I have used express and even passport several times) Thanks 1e+6! – Martin Sjöblom Feb 08 '20 at 11:37
  • I had the same problem but with Koa instead of express. Had to pass the Koa context as argument to the returned function. – Nice Books Feb 06 '22 at 11:33
25

You get 401 when you using different username, password input fields than the default ones. You have to provide that in your LocalStrategy like this :

passport.use(new LocalStrategy({
    usernameField: 'login',
    passwordField: 'password'
  },

  function(username, password, done) {
  ...
  }
));

Default is username and password I think. See the docs here.

user568109
  • 47,225
  • 17
  • 99
  • 123
  • Thanks for the thought, but I don't think that's it. I actually cut and pasted the form from those docs into my login page so I'm pretty sure I've got the fields named correctly. I'll update my question to include that info. – Oliver Dain Sep 09 '13 at 17:13
  • 3
    I'm struggling with this currently, I am finding that inside passport local the middle-ware is looking at req and trying to look at req.body, which when I console.log is undefined -- this returns a "bad message error" which never resolves for my post. basically authenticate isn't even happening – Catalyst Nov 14 '13 at 04:43
  • 1
    @Catalyst req.body is supposed to be set by the `bodyParser` middleware. – Julien Bérubé Feb 17 '14 at 18:00
  • 1
    Ah! Thanks a lot! I had email in the form. They should have mentioned it more obviously or at least manage documentation so they provide additional object for setting these parameters immediately and not using the default implementation. – Tommz Jul 31 '14 at 20:40
  • This should be marked as a correct answer!!!!! – Doctiger Apr 28 '22 at 15:01
10

I think you submit the form with username or password field empty.

If you fill both inputs then submit, LocalStrategy will be called.

Fancyoung
  • 2,313
  • 26
  • 32
  • Yes. It looks like a made progress. I was just playing around with the username field and didn't include the password. Once I put in the password I saw progress. It was so frustrating because I didn't care about the password. I was just testing things. I didn't know it was mandatory. – jack blank Nov 12 '22 at 09:03
8

For Express 4.x:

// Body parser
var bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({ extended: false })) // parse application/x-www-form-urlencoded
app.use(bodyParser.json()) // parse application/json

c.f. https://github.com/expressjs/body-parser

flaudre
  • 2,358
  • 1
  • 27
  • 45
5

I also banged my head for the same problem for few hours. I was having everything in place as told by other answers like 1. Form was returning all fields. 2. body parser was setup correctly with extended:true 3. Passport with every setting and configuration.

But i have changed username field with phoneNumber and password. By changing code like below solved my problem

  passport.use('login', new LocalStrategy(
    {usernameField:'phoneNumber',
    passwordField:'password'},
    function (username, password, done) {

    // your login goes here
    })

);

If anyone want i can write the whole authentication procedure for phonenumber using mongodb and passport-local.

Thanks @user568109

kamal pandey
  • 147
  • 1
  • 4
4

My problem was, that I had the enctype='multipart/form-data'. Just change that to multipart='urlencoded'. And I think that will solve it.

Ognjen
  • 85
  • 1
  • 2
  • 9
  • 1
    Thanks! Was about to write my own auth from scratch. Saved me tons of time. For some reason this package combo of Passport `"passport": "^0.3.2", "passport-http": "^0.3.0", "passport-local": "^1.0.0",` is not working with `enctype='multipart/form-data'` attribute in the login form. – larrydalmeida Jan 25 '17 at 11:51
2

To capture both username and passport make sure to add:

app.use(express.urlencoded());

In my case, I was using app.use(express.json()); and was posting the username and password from <form>.

A-Sharabiani
  • 17,750
  • 17
  • 113
  • 128
0

It's possible your request wasn't formatted properly (particularly the body of it) and your username and password weren't being sent when you thought they were.

Here is an example of an API call wrapper that enforces your request body is parsed as json:

Api = {};
Api.request = (route, options) => {
  options = options || {};

  options.method = options.method || 'GET';
  options.credentials = 'include';

  if (options.method === 'POST') {
    options.headers = {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    };
    options.body = JSON.stringify(options.data) || '{}';
  }

  fetch(absoluteUrl + '/api/' + route, options)
    .then((response) => response.json())
    .then(options.cb || (() => {}))
    .catch(function(error) {
      console.log(error);
    });
};

It can be used this way:

Api.request('login', {
  data: {
    username: this.state.username,
    password: this.state.password
  },
  method: 'POST',
  cb: proxy((user) => {
    console.log(user);
  }, this)
});
Max Hudson
  • 9,961
  • 14
  • 57
  • 107
0

For me everything was setup properly.. The issue was that I was collecting form data and then creating a json from form data and send it like JSON.stringify(formdatajson) to server. (For me login form was a popup on screen)

I Found my mistake by debugging passportjs...

If you also have same problem and none of the above solution seems working for you then debug passportjs.

open

strategy.js

put debugger in below method.

Strategy.prototype.authenticate 

Check what form data is coming for you. Hope this help ...

ifti
  • 649
  • 1
  • 11
  • 25
0
  1. npm install passport-local

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

According to passportjs.org and it worked for me!

Mujtaba Zaidi
  • 629
  • 1
  • 6
  • 14
0

Yet another possiblity: For me I had accidentally defined my routes before my body-parser middleware so body-parser was never active for the login route.

Joel Peltonen
  • 13,025
  • 6
  • 64
  • 100
0

Hope it always someone - in my case it was the fact that I tried I used passport middleware before cookieParser and express-session ones This was my code:

passportConfig(app);
app.use(cookieParser());
app.use(session({ secret: 'bla' }));

Changed passportConfig to be down and it worked:

app.use(cookieParser());
app.use(session({ secret: 'bla' }));
passportConfig(app);
Roni Litman
  • 923
  • 1
  • 8
  • 20
0

For me it wasn't working because i'd set different name in my login form to that in my passport.js ,It worked for me when i changed user.name in serializeUser to user.username.

passport.serializeUser(function (user, done) {
    done(null, user.username)
})

passport.deserializeUser(function (username, done) {
    Users.findOne({
        username: username
    }).then((user) => {
        if (!user) {
            return done(new Error("No such user"))
        }
        return done(null, user)
    }).catch((err) => {
        done(err)
    })
})
0

Check your form in your views if you included a name property in the form or input type and it corresponds to the arguments passed to the localStrategy callback function.

Prospee
  • 1
  • 3