Passport: TypeError: done is not a function

Created on 10 Oct 2015  路  21Comments  路  Source: jaredhanson/passport

I'm trying to use an OAuth1.0 provider, and after calling done in the verify function the serialization fails

passport.serializeUser = (user,done) ->
    done(null,user)
passport.deserializeUser = (obj,done) ->
    done(null,obj)
TypeError: done is not a function
    at Authenticator.passport.serializeUser (/app/scripts/jira.coffee:22:2, <js>:24:12)

any thoughts?

Most helpful comment

Found what was causing my case of done is not a function. When using passReqToCallback: true the parameters need to be updated:

function(req, username, password, done)

All 21 comments

+1?

5 months ago, any solution?

+1

+1

In my case is when using LocalStrategy, the error goes away when removing options (passReqToCallBack: true)

passport.use(new LocalStrategy({
  passReqToCallBack: true
},
  (username, password, done) => {
    User.findOne({ username }, (err, user) => {
      if (!user || !bcrypt.compareSync(password, user.password)) {
        done(null, false);
        return;
      }
      done(null, user);
    });
  }
));

+1?
any thoughts?

+1?

having this problem after authentication:

function(accessToken, refreshToken, profile, done) {
    process.nextTick(function () {
        return done(null,profile);
    });
}
));

Found what was causing my case of done is not a function. When using passReqToCallback: true the parameters need to be updated:

function(req, username, password, done)

stucked =(

any updates regarding this issue?
If I remove passReqToCallback : true, then I won't be able to fetch any form data and the actions fails.

This happened when adding a param to my Strategy in my passport.js file:

passport.use('customer-signup', new LocalStrategy({
        // by default, local strategy uses username and password, we will override with email
        usernameField : 'name',
        passwordField : 'password',
        emailField : 'email',           // added to get field from form
        passReqToCallback : true // allows us to pass back the entire request to the callback
    },
    function(req, name, password, email, done) {  // This is what causes the error

@rokit had it close.
I realized the extra param isn't being accounted for in the Strategy.js, so I opened up the file in the node_modules/passport/lib (for me, using the local strategy, it was node_modules/passport-local/lib/strategy.js) dir and made the changes:

function Strategy(options, verify) {
  if (typeof options == 'function') {
    verify = options;
    options = {};
  }
  if (!verify) { throw new TypeError('LocalStrategy requires a verify callback'); }

  this._usernameField = options.usernameField || 'username';
  this._passwordField = options.passwordField || 'password';
  this._emailField = options.emailField || 'email';    // HERE

  passport.Strategy.call(this);
  this.name = 'local';
  this._verify = verify;
  this._passReqToCallback = options.passReqToCallback;
}

/**
 * Inherit from `passport.Strategy`.
 */
util.inherits(Strategy, passport.Strategy);

Strategy.prototype.authenticate = function(req, options) {
  options = options || {};
  var username = lookup(req.body, this._usernameField) || lookup(req.query, this._usernameField);
  var password = lookup(req.body, this._passwordField) || lookup(req.query, this._passwordField);
  var email = lookup(req.body, this._emailField) || lookup(req.query, this._emailField);    // HERE

  if (!username || !password || !email) {     //HERE
    return this.fail({ message: options.badRequestMessage || 'Missing credentials' }, 400);
  }

  var self = this;

  function verified(err, user, info) {
    if (err) { return self.error(err); }
    if (!user) { return self.fail(info); }
    self.success(user, info);
  }

  try {
    if (self._passReqToCallback) {
      this._verify(req, username, password, email, verified);    // AND HERE
    } else {
      this._verify(username, password, email, verified); // Here if you don't want to pass the req
    }                                                   // I believe each param requires the req for verification though
  } catch (ex) {
    return self.error(ex);
  }
};

and viola! The error is gone, req is passed, verification passes...but now I've got to go and update every schema that uses this strategy to include my new param....

1st edit:

Back to the original question @tjsail33 . I think you will have to deserialize with a query:

passport.serializeUser(function(User, done) {
        done(null, User.id);
    });

    passport.deserializeUser(function(id, done) {
        users.User.findOne(obj, function(err, User) {
            done(err, User);
        });
    });

2nd Edit:

However, you can't serialize with an object either, unless that object is a mongo oid object. So, if you are trying to do what I am doing, and pass a different type of object, you have to identify it and create the exception. Here is what my serializing looks like:

passport.serializeUser(function(User, done) {
        if (typeof(User) == 'object'){
            console.log('starting customer session'); // This is where I finally found the error
            done(null, User._id);
        } else {
            console.log('starting client session');
            done(null, User.id);
        }
    });

    // used to deserialize the user
   // **edit2:** I made a mistake in thinking the id was still being passed as an object
    // You should sort through the object when serializing and pass that objects id
    passport.deserializeUser(function(id, done) {
        var isCustomer = false;
        users.User.findById(id, function(err, User) {
            if (User){
                done(err, User);
            } else{
                 isCustomer = true;
                 tryCustomer(isCustomer, id);
            }
        });
        // if the id doesnt match a user, search customers
        function tryCustomer(isCustomer, id){
            if (isCustomer, id){
                users.Customer.findById(id, function(err, Customer){
                    if (err) console.log(err);
                    done(err, Customer);
                });
            }
        }
    });

Since I was passing the customer object back, and passport will ONLY SERIALIZE OIDs, instead of passing id, when id is an object from the DB, passed id._id, and there you have a serialized other-type object

@kire73 I have followed your method for modifying strategy.js. My signup works perfectly .but I am having issues with my signin.

screenshot 3
screenshot 4
screenshot 5

As soon as i submit the signin form i get this error:
screenshot 6

@gerard080395 It looks like you are missing a callback somewhere. In your strategy.js file, are you adding all of your new fields to the callback?

function Strategy(options, verify) {
  if (typeof options == 'function') {
    verify = options;
    options = {};
  }
  if (!verify) { throw new TypeError('LocalStrategy requires a verify callback'); }

  this._usernameField = options.usernameField || 'username';
  this._passwordField = options.passwordField || 'password';
  this._emailField = options.emailField || 'email';         // I only added email, but you need to add each field                                          //                                                                                       you want to validate with

  passport.Strategy.call(this); // Here is the callback
  this.name = 'local';
  this._verify = verify;
  this._passReqToCallback = options.passReqToCallback;
}

@kire73 thanks for replying. I think i added all the fields. But still the same issue.
Here is my strategy.js code

/**
 * Module dependencies.
 */
var passport = require('passport-strategy')
  , util = require('util')
  , lookup = require('./utils').lookup;


/**
 * `Strategy` constructor.
 *
 * The local authentication strategy authenticates requests based on the
 * credentials submitted through an HTML-based login form.
 *
 * Applications must supply a `verify` callback which accepts `username` and
 * `password` credentials, and then calls the `done` callback supplying a
 * `user`, which should be set to `false` if the credentials are not valid.
 * If an exception occured, `err` should be set.
 *
 * Optionally, `options` can be used to change the fields in which the
 * credentials are found.
 *
 * Options:
 *   - `usernameField`  field name where the username is found, defaults to _username_
 *   - `passwordField`  field name where the password is found, defaults to _password_
 *   - `passReqToCallback`  when `true`, `req` is the first argument to the verify callback (default: `false`)
 *
 * Examples:
 *
 *     passport.use(new LocalStrategy(
 *       function(username, password, done) {
 *         User.findOne({ username: username, password: password }, function (err, user) {
 *           done(err, user);
 *         });
 *       }
 *     ));
 *
 * @param {Object} options
 * @param {Function} verify
 * @api public
 */
function Strategy(options, verify) {
  if (typeof options == 'function') {
    verify = options;
    options = {};
  }
  if (!verify) { throw new TypeError('LocalStrategy requires a verify callback'); }
  this._usernameField = options.usernameField || 'username';
  this._passwordField = options.passwordField || 'password';

  this._emailField = options.emailField || 'email';
  this._surnameField = options.surnameField || 'surname';
  this._addressField = options.addressField || 'address';
  this._telField = options.telField || 'telephone';
  this._nameField = options.nameField || 'name';


  passport.Strategy.call(this);
  this.name = 'local';
  this._verify = verify;
  this._passReqToCallback = options.passReqToCallback;
}

/**
 * Inherit from `passport.Strategy`.
 */
util.inherits(Strategy, passport.Strategy);

/**
 * Authenticate request based on the contents of a form submission.
 *
 * @param {Object} req
 * @api protected
 */
Strategy.prototype.authenticate = function(req, options) {
  options = options || {};
  var username = lookup(req.body, this._usernameField) || lookup(req.query, this._usernameField);
  var password = lookup(req.body, this._passwordField) || lookup(req.query, this._passwordField);

  var email = lookup(req.body, this._emailField) || lookup(req.query, this._emailField);
  var surname = lookup(req.body, this._surnameField) || lookup(req.query, this._surnameField);
  var address = lookup(req.body, this._addressField) || lookup(req.query, this._addressField);
  var telephone = lookup(req.body, this._telField) || lookup(req.query, this._telField);
  var name = lookup(req.body, this._nameField) || lookup(req.query, this._nameField);

  if (!username || !password || !surname || !address || !telephone || !name) {
    return this.fail({ message: options.badRequestMessage || 'Missing credentials' }, 400);
  }

  var self = this;

  function verified(err, user, info) {
    if (err) { return self.error(err); }
    if (!user) { return self.fail(info); }
    self.success(user, info);
  }

  try {
    if (self._passReqToCallback) {
      this._verify(req, username, password,surname,address,telephone,name, verified);
    } else {
      this._verify(username, password,surname,address,telephone,name,verified);
    }
  } catch (ex) {
    return self.error(ex);
  }
};


/**
 * Expose `Strategy`.
 */
module.exports = Strategy;

@gerard080395 It seems to be in order. I would try placing a couple of console.log()s in your serialize and deserialize blocks. This is what carries the session data, so if your signup works but not signin, chances are the id being passed in the serialize section is not the is of the user object you are trying to authenticate. Let me know what you find there.

I fixed this issue for me by changing my
const passport = require('passport-local);
in my server.js to
const passport = require('passport');

Hey guys,

Is this about passing extra parameter from the form to the passport.js? I used req.body. to pass extra field like 'Role' and used that in my code. That way I didn't have to edit the passport's code and I was able to add that information in my mongo database.

I might be sounding completely stupid but this is what I understood from your discussion above.

Abi

@abhishek-b-agarwal Best advice all week! You just made my day!

@eminds11 Cheers! Happy my comment helped you!

This can be solved without editing the strategy.js document. The error results because there aren't enough arguments in the callback function. You need to have at least 4 if you are using passReqToCallback: true, and they should be in the following order: }, function(req, username, password, done) {

In your passport.js config file, passport.use(new LocalStrategy) callback function depending on which strategy you are using you will need a certain number of arguments. I just has to add "req" as the first argument in mine.

` passport.use(new LocalStrategy({
passReqToCallback: true
},
function (req, apikey, done) {
//ADD REQ UP HERE
// asynchronous verification, for effect...
process.nextTick(function () {

            // Find the user by username.  If there is no user with the given
            // username, or the password is not correct, set the user to `false` to
            // indicate failure and set a flash message.  Otherwise, return the
            // authenticated `user`.
            findByApiKey(apikey, function (err, user) {
                if (err) {
                    return done(err);
                }
                if (!user) {
                    return done(null, false, {
                        message: 'Unknown apikey : ' + apikey
                    });
                }
                // if (user.password != password) { return done(null, false, { message: 'Invalid password' }); }
                return done(null, user);
            })
        });
    }
));`
Was this page helpful?
0 / 5 - 0 ratings

Related issues

prakhar897 picture prakhar897  路  6Comments

angel1st picture angel1st  路  5Comments

andrewbanchich picture andrewbanchich  路  4Comments

itaditya picture itaditya  路  5Comments

xingrz picture xingrz  路  5Comments