GithubHelp home page GithubHelp logo

saintedlama / passport-local-mongoose Goto Github PK

View Code? Open in Web Editor NEW
1.2K 21.0 296.0 1013 KB

Passport-Local Mongoose is a Mongoose plugin that simplifies building username and password login with Passport

License: MIT License

JavaScript 97.47% Dockerfile 1.77% TypeScript 0.76%
passport mongoose-plugin javascript

passport-local-mongoose's Introduction

Passport-Local Mongoose

Passport-Local Mongoose is a Mongoose plugin that simplifies building username and password login with Passport.

Node.js CI Coverage Status

Tutorials

Michael Herman gives a comprehensible walk through for setting up mongoose, passport, passport-local and passport-local-mongoose for user authentication in his blog post User Authentication With Passport.js

Installation

> npm install passport-local-mongoose

Passport-Local Mongoose does not require passport or mongoose dependencies directly but expects you to have these dependencies installed.

In case you need to install the whole set of dependencies

> npm install passport mongoose passport-local-mongoose

Usage

Plugin Passport-Local Mongoose

First you need to plugin Passport-Local Mongoose into your User schema

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const passportLocalMongoose = require('passport-local-mongoose');

const User = new Schema({});

User.plugin(passportLocalMongoose);

module.exports = mongoose.model('User', User);

You're free to define your User how you like. Passport-Local Mongoose will add a username, hash and salt field to store the username, the hashed password and the salt value.

Additionally, Passport-Local Mongoose adds some methods to your Schema. See the API Documentation section for more details.

Configure Passport/Passport-Local

You should configure Passport/Passport-Local as described in the Passport Guide.

Passport-Local Mongoose supports this setup by implementing a LocalStrategy and serializeUser/deserializeUser functions.

To setup Passport-Local Mongoose use this code

// requires the model with Passport-Local Mongoose plugged in
const User = require('./models/user');

// use static authenticate method of model in LocalStrategy
passport.use(new LocalStrategy(User.authenticate()));

// use static serialize and deserialize of model for passport session support
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

Make sure that you have mongoose connected to mongodb and you're done.

Simplified Passport/Passport-Local Configuration

Starting from version 0.2.1, passport-local-mongoose adds a helper method createStrategy as static method to your schema. The createStrategy is responsible to setup passport-local LocalStrategy with the correct options.

const User = require('./models/user');

// CHANGE: USE "createStrategy" INSTEAD OF "authenticate"
passport.use(User.createStrategy());

passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

The reason for this functionality is that when using the usernameField option to specify an alternative usernameField name, for example "email" passport-local would still expect your frontend login form to contain an input field with name "username" instead of email. This can be configured for passport-local but this is double the work. So we got this shortcut implemented.

Async/Await

Starting from version 5.0.0, passport-local-mongoose is async/await enabled by returning Promises for all instance and static methods except serializeUser and deserializeUser.

const user = new DefaultUser({username: 'user'});
await user.setPassword('password');
await user.save();
const { user } = await DefaultUser.authenticate()('user', 'password');

Options

When plugging in Passport-Local Mongoose plugin, additional options can be provided to configure the hashing algorithm.

User.plugin(passportLocalMongoose, options);

Main Options

  • saltlen: specifies the salt length in bytes. Default: 32
  • iterations: specifies the number of iterations used in pbkdf2 hashing algorithm. Default: 25000
  • keylen: specifies the length in byte of the generated key. Default: 512
  • digestAlgorithm: specifies the pbkdf2 digest algorithm. Default: sha256. (get a list of supported algorithms with crypto.getHashes())
  • interval: specifies the interval in milliseconds between login attempts, which increases exponentially based on the number of failed attempts, up to maxInterval. Default: 100
  • maxInterval: specifies the maximum amount of time an account can be locked. Default 300000 (5 minutes)
  • usernameField: specifies the field name that holds the username. Defaults to 'username'. This option can be used if you want to use a different field to hold the username for example "email".
  • usernameUnique: specifies if the username field should be enforced to be unique by a mongodb index or not. Defaults to true.
  • saltField: specifies the field name that holds the salt value. Defaults to 'salt'.
  • hashField: specifies the field name that holds the password hash value. Defaults to 'hash'.
  • attemptsField: specifies the field name that holds the number of login failures since the last successful login. Defaults to 'attempts'.
  • lastLoginField: specifies the field name that holds the timestamp of the last login attempt. Defaults to 'last'.
  • selectFields: specifies the fields of the model to be selected from mongodb (and stored in the session). Defaults to 'undefined' so that all fields of the model are selected.
  • usernameCaseInsensitive: specifies the usernames to be case insensitive. Defaults to 'false'.
  • usernameLowerCase: convert username field value to lower case when saving an querying. Defaults to 'false'.
  • populateFields: specifies fields to populate in findByUsername function. Defaults to 'undefined'.
  • encoding: specifies the encoding the generated salt and hash will be stored in. Defaults to 'hex'.
  • limitAttempts: specifies whether login attempts should be limited and login failures should be penalized. Default: false.
  • maxAttempts: specifies the maximum number of failed attempts allowed before preventing login. Default: Infinity.
  • unlockInterval: specifies the interval in milliseconds, which is for unlock user automatically after the interval is reached. Defaults to 'undefined' which means deactivated.
  • passwordValidator: specifies your custom validation function for the password in the form:
    passwordValidator = function(password,cb) {
      if (someValidationErrorExists(password)) {
        return cb('this is my custom validation error message')
      }
      // return an empty cb() on success
      return cb()
    }
    Default: validates non-empty passwords.
  • passwordValidatorAsync: specifies your custom validation function for the password with promises in the form:
    passwordValidatorAsync = function(password) {
      return someAsyncValidation(password)
        .catch(function(err){
          return Promise.reject(err)
        })
    }
  • usernameQueryFields: specifies alternative fields of the model for identifying a user (e.g. email).
  • findByUsername: Specifies a query function that is executed with query parameters to restrict the query with extra query parameters. For example query only users with field "active" set to true. Default: function(model, queryParameters) { return model.findOne(queryParameters); }. See the examples section for a use case.

Attention! Changing any of the hashing options (saltlen, iterations or keylen) in a production environment will prevent existing users from authenticating!

Error Messages

Override default error messages by setting options.errorMessages.

  • MissingPasswordError: 'No password was given'
  • AttemptTooSoonError: 'Account is currently locked. Try again later'
  • TooManyAttemptsError: 'Account locked due to too many failed login attempts'
  • NoSaltValueStoredError: 'Authentication not possible. No salt value stored'
  • IncorrectPasswordError: 'Password or username are incorrect'
  • IncorrectUsernameError: 'Password or username are incorrect'
  • MissingUsernameError: 'No username was given'
  • UserExistsError: 'A user with the given username is already registered'

Hash Algorithm

Passport-Local Mongoose use the pbkdf2 algorithm of the node crypto library. Pbkdf2 was chosen because platform independent (in contrary to bcrypt). For every user a generated salt value is saved to make rainbow table attacks even harder.

Examples

For a complete example implementing a registration, login and logout see the login example.

API Documentation

Instance methods

setPassword(password, [cb])

Sets a user password. Does not save the user object. If no callback cb is provided a Promise is returned.

changePassword(oldPassword, newPassword, [cb])

Changes a user's password hash and salt, resets the user's number of failed password attempts and saves the user object (everything only if oldPassword is correct). If no callback cb is provided a Promise is returned. If oldPassword does not match the user's old password, an IncorrectPasswordError is passed to cb or the Promise is rejected.

authenticate(password, [cb])

Authenticates a user object. If no callback cb is provided a Promise is returned.

resetAttempts([cb])

Resets a user's number of failed password attempts and saves the user object. If no callback cb is provided a Promise is returned. This method is only defined if options.limitAttempts is true.

Callback Arguments

  • err
    • null unless the hashing algorithm throws an error
  • thisModel
    • the model getting authenticated if authentication was successful otherwise false
  • passwordErr
    • an instance of AuthenticationError describing the reason the password failed, else undefined.

Using setPassword() will only update the document's password fields, but will not save the document. To commit the changed document, remember to use Mongoose's document.save() after using setPassword().

Error Handling

  • IncorrectPasswordError: specifies the error message returned when the password is incorrect. Defaults to 'Incorrect password'.
  • IncorrectUsernameError: specifies the error message returned when the username is incorrect. Defaults to 'Incorrect username'.
  • MissingUsernameError: specifies the error message returned when the username has not been set during registration. Defaults to 'Field %s is not set'.
  • MissingPasswordError: specifies the error message returned when the password has not been set during registration. Defaults to 'Password argument not set!'.
  • UserExistsError: specifies the error message returned when the user already exists during registration. Defaults to 'User already exists with name %s'.
  • NoSaltValueStored: Occurs in case no salt value is stored in the MongoDB collection.
  • AttemptTooSoonError: Occurs if the option limitAttempts is set to true and a login attept occures while the user is still penalized.
  • TooManyAttemptsError: Returned when the user's account is locked due to too many failed login attempts.

All those errors inherit from AuthenticationError, if you need a more general error class for checking.

Static methods

Static methods are exposed on the model constructor. For example to use createStrategy function use

const User = require('./models/user');
User.createStrategy();
  • authenticate() Generates a function that is used in Passport's LocalStrategy
  • serializeUser() Generates a function that is used by Passport to serialize users into the session
  • deserializeUser() Generates a function that is used by Passport to deserialize users into the session
  • register(user, password, cb) Convenience method to register a new user instance with a given password. Checks if username is unique. See login example.
  • findByUsername() Convenience method to find a user instance by it's unique username.
  • createStrategy() Creates a configured passport-local LocalStrategy instance that can be used in passport.

Examples

Allow only "active" users to authenticate

First, we define a schema with an additional field active of type Boolean.

const UserSchema = new Schema({
  active: Boolean
});

When plugging in Passport-Local Mongoose, we set usernameUnique to avoid creating a unique mongodb index on field username. To avoid non active users being queried by mongodb, we can specify the option findByUsername that allows us to restrict a query. In our case we want to restrict the query to only query users with field active set to true. The findByUsername MUST return a Mongoose query.

UserSchema.plugin(passportLocalMongoose, {
  // Set usernameUnique to false to avoid a mongodb index on the username column!
  usernameUnique: false,

  findByUsername: function(model, queryParameters) {
    // Add additional query parameter - AND condition - active: true
    queryParameters.active = true;
    return model.findOne(queryParameters);
  }
});

To test the implementation, we can simply create (register) a user with field active set to false and try to authenticate this user in a second step:

const User = mongoose.model('Users', UserSchema);

User.register({username:'username', active: false}, 'password', function(err, user) {
  if (err) { ... }

  const authenticate = User.authenticate();
  authenticate('username', 'password', function(err, result) {
    if (err) { ... }

    // Value 'result' is set to false. The user could not be authenticated since the user is not active
  });
});

Updating from 1.x to 2.x

The default digest algorithm was changed due to security implications from sha1 to sha256. If you decide to upgrade a production system from 1.x to 2.x, your users will not be able to login since the digest algorithm was changed! In these cases plan some migration strategy and/or use the sha1 option for the digest algorithm.

License

Passport-Local Mongoose is licensed under the MIT license.

passport-local-mongoose's People

Contributors

bachp avatar backflip avatar buehler avatar cheyako avatar chrishubinger avatar fhemberger avatar gentlee avatar github-actions[bot] avatar hensansi avatar iheartweb avatar jeremykennedy avatar jtremback avatar kheftel avatar krittaboon avatar minecrafter avatar mjhea0 avatar mvoorberg avatar neozaru avatar niftylettuce avatar nikwen avatar saintedlama avatar seport avatar smcmurray avatar stebogit avatar techjeffharris avatar toastshaman avatar v4l3r10 avatar vck3000 avatar vergenzt avatar vkarpov15 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

passport-local-mongoose's Issues

Authentication crashes if salt is not present

I have a system where some users stored in the database do not have accounts, i.e. they do not have a hash and salt. If I try to log in as such a user, the authentication method throws an exception. I believe that a more accurate response would be to simply not authenticate the user if not both hash and salt are present.

TypeError: undefined is not a function

When working with this module and node-restful I receive the following error: "TypeError: undefined is not a function".

I have been able to workaround it by adding a single line to schema.statics.register

if(typeof(cb) === 'undefined') cb = function(){};

Can this be included or a better handling of the error be implemented?

Set BadRequest status to 400

Just a suggestion, it seems that a Bad Request error type should have the appropriate BadReqest HTTP status!

Thoughts?

Support multiple encryption types

I have a usecase that I think may not be so unique, I have to support multiple forms of encryption on passwords to log users in due to historical userdata that has those forms of encryption in them until all users move over to the new format.

However, there are two forms of encryption that are secure enough that users need not move off of it bcrypt and pbkdf2

Currently I need to support the following schemes:

  • bcrypt
  • pbkdf2
  • md5
  • sha256

save() concurrency issue when using options.limitAttempts

Hello,
While using your plugin and implementing a "delete my account" feature, I can ran into a Mongoose error "VersionError: No matching document found".

When a user wants to delete his account, my app opens a modal and asks for his password. If the password is matching (using passport-local-mongoose 'authenticate'), then I set "status: 'deleted'" on the User document and save it. I'm using options.limitAttempts, so passport-local-mongoose performs a first save beforehand to update lastLoginField and attemptsField, but does not use a callback.
Therefore, If "my" second save comes too quickly after "yours", I get this mongoose version conflict.

Could you nest the callback of authenticate inside the call to self.save() ?

~line 105:

        if (hash === self.get(options.hashField)) {
            if (options.limitAttempts){
              self.set(options.lastLoginField, Date.now());
              self.set(options.attemptsField, 0);
              self.save();
            }
            return cb(null, self);
        } else {
           โ€ฆ
        }

=>

        if (hash === self.get(options.hashField)) {
            if (options.limitAttempts){
              self.set(options.lastLoginField, Date.now());
              self.set(options.attemptsField, 0);
              self.save(cb);
            } else {
              return cb(null, self);
            }
        } else {
           โ€ฆ
        }

Login example discrepancies

app.js line 41 uses the deprecated

passport.use(new LocalStrategy(Account.authenticate()));

instead of

passport.use(new LocalStrategy(Account.createStrategy()));

findOne method not found

Stacktrack:

at Object.schema.statics.findByUsername (/home/ubuntu/MoojicDashboard/node_modules/passport-local-mongoose/lib/passport-local-mongoose.js:170:26) at Strategy._verify (/home/ubuntu/MoojicDashboard/node_modules/passport-local-mongoose/lib/passport-local-mongoose.js:100:18) at Strategy.authenticate (/home/ubuntu/MoojicDashboard/node_modules/passport-local-mongoose/node_modules/passport-local/lib/strategy.js:90:12) at attempt (/home/ubuntu/MoojicDashboard/node_modules/passport/lib/middleware/authenticate.js:341:16) at authenticate (/home/ubuntu/MoojicDashboard/node_modules/passport/lib/middleware/authenticate.js:342:7) at Layer.handle [as handle_request] (/home/ubuntu/MoojicDashboard/node_modules/express/lib/router/layer.js:76:5) at next (/home/ubuntu/MoojicDashboard/node_modules/express/lib/router/route.js:100:13) at Route.dispatch (/home/ubuntu/MoojicDashboard/node_modules/express/lib/router/route.js:81:3) at Layer.handle [as handle_request] (/home/ubuntu/MoojicDashboard/node_modules/express/lib/router/layer.js:76:5) at /home/ubuntu/MoojicDashboard/node_modules/express/lib/router/index.js:227:24

Model file;

var passportLocalMongoose = require('passport-local-mongoose');

var UsersSchema = new Schema({
email: { type: String, required: true },
creationDate: { type: Date, default: Date.now },
updatedDate: { type: Date, default: Date.now },
firstName: { type: String, required: false },
middleName: { type: String, required: false },
last_name: { type: String, required: false },
contact: { type: String, required: false }
}, { collection: constants.USER_COLLECTION, autoIndex: false });

UsersSchema.plugin(passportLocalMongoose);

passport.use(UsersSchema.statics.createStrategy());
// use static serialize and deserialize of model for passport session support
passport.serializeUser(UsersSchema.statics.serializeUser());
passport.deserializeUser(UsersSchema.statics.deserializeUser());

The problem in coming from the library.

From passport-local-mongoose.js file:

schema.statics.findByUsername = function(username, cb) { var queryParameters = {}; // if specified, convert the username to lowercase if (username !== undefined && options.usernameLowerCase) { username = username.toLowerCase(); }
queryParameters[options.usernameField] = username;

    var query = this.findOne(queryParameters); <-- This line giving the error.
    if (options.selectFields) {
        query.select(options.selectFields);
    }

This method should exist on mongoose model by default. What i am doing wrong?

Troubles using "usernameField" to set another username

Hi,

When I use "usernameField" for change the username to "email", for example, passport always issues a Unauthorized 401 response.

When I register a new user and check mongodb, there is a record for the new generated username with email as the field.

Can you help me on this?

Thanks

Account.register silently fails (no err)?

I'm using this, just like in the example. But it's either hanging somewhere indefinitely or just failing without creating an error. :(

app.post('/register', function(req, res) {
    console.log('registering a new account');
    Account.register(new Account({ username : req.body.username }), req.body.password, function(err, account) {
        if (err) {
            console.log(err); // Never gets called.
        } else {
            res.redirect('/'); // Never reaches this either.
            console.log('Successfully registered a new account!');
        }
    });
});

nested user accounts

Passport-local-mongoose is great, many thanks for the package.

I want to have a User model, which holds the information like username, hash, salt, ...,
and I want to have a Group model, which holds an array of Users (with nested documents and not ObjectIds). Then I want the User.username field to be globally unique (as it is in the package right now - using the User.register() function), but I don't want to have a global collection of Users (because then Group.users will have to be an array of ObjectIds, which doesn't fit my models and best practices to work with mongo - since my Users don't have any meaning outside the scope of the Group).

Is there a way to do it with passport-local-mongoose?

Create a logout example

I'm not really sure I'm fully understanding the system here. I noticed there's no logout example and I'm somewhat confused. Can anyone add a couple more good examples?

Password Change Method

Can you implement a method that will salt, hash and store a new password associated with a particular user account?

Thanks!

.

I have been struggling with the error messages. Are they connect-flash messages? I have tried this

app.post('/login', function(req, res, next) {
  passport.authenticate('local', function(err, user, info) {
    if (err) { return res.send(info); }
    if (!user) { return res.send(info); }
    req.logIn(user, function(err) {
      if (err) { return res.send(info); }
      return res.send(user.username);
    });
  })(req, res, next);
});

But then I will get passportJS error messages like "bad credentials". I would like to get the incorrectPasswordError or incorrectUsernameError, just returned in plain json. I use client side templating, so I want to redirect on the client. How can I access the error messages?

.

I am specifying email and password matchers in the mongoose schema.

var Account = new Schema({
   username: {type: String, match: [/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email address']},
   password: {type: String, match: [/(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/, 'Password not valid']}
});

This works when I am trying to register a bad email address, but it doesn't work for the password.

Error during user sign up

I'm trying to get registration working in an app on the MEAN stack, but I'm getting a 500 error when submitting the signup form. The error says Object #<IncomingMessage> has no method 'toLowerCase'. The stack trace is:

TypeError: Object #<IncomingMessage> has no method 'toLowerCase';
    at Function.schema.statics.findByUsername (/Users/username/node_app/node_modules/passport-local-mongoose/lib/passport-local-mongoose.js:193:33)
    at Strategy._verify (/Users/username/node_app/node_modules/passport-local-mongoose/lib/passport-local-mongoose.js:127:18)
    at Strategy.authenticate (/Users/username/node_app/node_modules/passport-local/lib/strategy.js:88:12)
    at attempt (/Users/username/node_app/node_modules/passport/lib/middleware/authenticate.js:341:16)
    at authenticate (/Users/username/node_app/node_modules/passport/lib/middleware/authenticate.js:342:7)
    at Layer.handle [as handle_request] (/Users/username/node_app/node_modules/express/lib/router/layer.js:82:5)
    at next (/Users/username/node_app/node_modules/express/lib/router/route.js:100:13)
    at Route.dispatch (/Users/username/node_app/node_modules/express/lib/router/route.js:81:3)
    at Layer.handle [as handle_request] (/Users/username/node_app/node_modules/express/lib/router/layer.js:82:5)
    at /Users/username/node_app/node_modules/express/lib/router/index.js:234:24

I was able to log what the username argument was in findByUsername and it's the entire request object sent to the server, instead of just the username.

Here's my user model:

var mongoose = require('mongoose'),
  Schema = mongoose.Schema,
  passportLocalMongoose = require('passport-local-mongoose');

var User = new Schema({});
User.plugin(passportLocalMongoose, {
  usernameField: 'email',
  usernameUnique: true,
  usernameLowerCase: true
});

module.exports = mongoose.model('User', User);

This is my mongoose and passport config in app.js:

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/DB_NAME');

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

var passport = require('passport');
var strategy = User.createStrategy();
passport.use(strategy);
strategy._passReqToCallback = true;

passport.use('local-signup', strategy, function(req, email, password, done) {
  process.nextTick(function(){
    User.findOne({'email': email}, function(err, user) {
      if (err){
        console.log('Error in SignUp: '+err);
        return done(err);
      }
      if (user) {
        console.log('User already exists');
        return done(null, false,
           req.flash('message','User Already Exists'));
      } else {
        var newUser = new User();
        newUser.email = email;
        newUser.password = createHash(password);
        newUser.save(function(err) {
          if (err){
            console.log('Error in Saving user: '+err);
            throw err;
          }
          console.log('User Registration succesful');
          return done(null, newUser);
        });
      }
    });
  });
});

passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

var expressSession = require('express-session');
app.use(expressSession({secret: 'secretKey'}));
app.use(passport.initialize());
app.use(passport.session());

These are the routes to handle login and signup:

router.post('/signup', passport.authenticate('local-signup'), function(req, res) {
  res.send(req.user);
});

router.post('/login', passport.authenticate('local'), function(req, res) {
  res.send(req.user);
});

And in my Angular controller, this is how I send the request to '/signup':

// this.email = "[email protected]";
// this.password = "testpass";
$http.post('/signup', {email: this.email, password: this.password});

What am I doing wrong here? Why is the request object being passed into the findByUserName function?

login example: "Cannot find module passport-local"

I receive "cannot find module passport-local" after doing the following:

git clone https://github.com/saintedlama/passport-local-mongoose.git
cd ./passport-local-mongoose/examples/login/
npm install
node app

I then try to explicitly do a "npm install passport-local", which succeeds, but still get the error. Am I missing something here?

mongoose save lack item

model:

var mongoose = require('mongoose'),
    passportLocalMongoose = require('passport-local-mongoose');

var userSchema = mongoose.Schema({
    local : {
        name: String,
        email: String,
        password: String
    }
});

userSchema.plugin(passportLocalMongoose, { usernameField: "email",
                                           usernameLowerCase: true });

var User = mongoose.model('User', userSchema);

module.exports = User;

control:

var authSignup = function(req, res, next) {
        User.register(new User({ name: req.body.name, email: req.body.email }),
                      req.body.password, function(err) {
                          if (err) {
                              req.session.flash = {
                                  type: 'danger',
                                  intro: 'Signup error!',
                                  message: err.message
                              };
                              res.redirect(303, 'new');
                              return;
                          }
                          passport.authenticate('local')(req, res, function() {
                              res.redirect(303, '/users/' + req.user._id);
                          });
                      });
    };

user output:

user: { _id: 55589a33d09d448b47cad663,
salt: 'cac36dd9c1004ab8452ccbeb0e58539eb694260fbf2416e6a3a6a13cf06295a2',
hash: 'f6dee71ac1e88e218e970c63bc27adaf244ff9075d3a1dbb44c30c8359572f1cf234759cae649fdac2bdea6abf91682f4a523709aaa8177594df27cb6312ecf764954572da2b25c22be27036807bc04c689284df0d9d1b27a552da967dd2022bd48d779abfbcfb594065d21a1aedc39068597c899f343b7b2936b0d2a4246423e8da32c52fc0c13b79a5cb29066478316d51b2b5fd646bd68c0e846b01dc1f543eb9bf9b27634ff98f4dc14f352f790485d80b2b3ed77c756e04889a7fa521fc34f8f269e761059d93653e48c7b55a7b51736aaa7ce67cf5d4bb5cea1e38d9400f8c06236f6d6618f20f7bcabd8e5d1cb0d0720038c26a2b25c6ada7221e73cb8cfb694d0f9e08ac289b79ec23fd1a3c096e1ae44cdadfbcb23e345c557d13b78f07d9845c61b3340924c6e0073e6a7c11fb43b58fcdeac4aff8a83987e9478afcfb5eaef980479910ef808f2e0686aeaa36c6118125d64b485fac7b533602af0751fc46409b419bfc366c02291ed8de9b777e74e6fdb00b52b2c70025db156b2f6dd0476f6fc6d30723d73dc63fdb56c847a177ac539d3c567746e2790186e078c88485d3db27b54c6353bdd1695e8dd0c2a4feaae43dde8691dbb54995d68fbf4ce322055247f891e79daf0f31781094dad799c77799a246ddfc74038fb3a3e6da5fe8fc8e9ced0037ab3073868194c4171b18883b04b29f77e2ca2189cdf4',
email: '[email protected]',
__v: 0,
local: {} }

The item name is lost. I don't know why.

my package.json:

 "dependencies": {
    "bcrypt-nodejs": "0.0.3",
    "body-parser": "^1.12.4",
    "connect": "^2.29.2",
    "connect-flash": "^0.1.1",
    "cookie-parser": "^1.3.4",
    "express": "^4.12.3",
    "express-handlebars": "^2.0.1",
    "express-session": "^1.11.2",
    "mongoose": "^4.0.3",
    "morgan": "^1.5.3",
    "passport": "^0.2.1",
    "passport-local": "^1.0.0",
    "passport-local-mongoose": "^1.0.0",
    "passport-remember-me": "0.0.1",
    "session-mongoose": "^0.5.2"
  },

Need your help, thank you very much!

selectFields is being ignored

I haven't got selectFields ever working, hash and salt is being included on each query.

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var util = require('util');
var assert = require('assert');
var passportLocalMongoose = require('passport-local-mongoose');

mongoose.set('debug', true);
mongoose.connect('mongodb://localhost/gh2735');

var UserSchema = new mongoose.Schema({
  username: { type: String, lowercase: true, required: true, select: true },
  email: { type: String, lowercase: true, required: true, select: true },
  salt: { type: String, select: false },
  hash: { type: String, select: false }
});


UserSchema.plugin(passportLocalMongoose, {
  selectFields: 'username email -hash -salt'
});

var UserModel = mongoose.model('User', UserSchema);

var user = new UserModel({ username: 'vkarpov15', email: '[email protected]', salt: 'yes please', hash: 'LV426' });

user.save(function(error, user) {
    assert.ifError(error);
    UserModel.findOne({ _id: user._id })
    .exec(function(error, user) {
      assert.ifError(error);
      assert.ok(!user.salt);
      assert.ok(!user.hash);
      console.log(util.inspect(user));
      process.exit(0);
    });
  });

this code outputs:

Mongoose: users.insert({ username: 'vkarpov15', email: '[email protected]', salt: 'yes please', hash: 'LV426', _id: ObjectId("551bd2af69fda1530e95d63c"), __v: 0 })   
Mongoose: users.findOne({ _id: ObjectId("551bd2af69fda1530e95d63c") }) { fields: undefined }  

assert.js:86
  throw new assert.AssertionError({
        ^
AssertionError: false == true
    at EventEmitter.<anonymous> (/home/jussi/development/mongoosebug/gh-2735.js:32:14)
    at EventEmitter.<anonymous> (/home/jussi/development/mongoosebug/node_modules/mongoose/node_modules/mpromise/lib/promise.js:175:45)
    at EventEmitter.emit (events.js:107:17)
    at Promise.safeEmit (/home/jussi/development/mongoosebug/node_modules/mongoose/node_modules/mpromise/lib/promise.js:81:21)
    at Promise.fulfill (/home/jussi/development/mongoosebug/node_modules/mongoose/node_modules/mpromise/lib/promise.js:94:24)
    at Promise.resolve (/home/jussi/development/mongoosebug/node_modules/mongoose/lib/promise.js:113:23)
    at /home/jussi/development/mongoosebug/node_modules/mongoose/lib/query.js:1169:16
    at /home/jussi/development/mongoosebug/node_modules/mongoose/node_modules/kareem/index.js:103:16
    at process._tickCallback (node.js:355:11)

Example code taken from: Automattic/mongoose#2735

Port for Rethinkdb

Hi,

This is not the most appropriate channel to ask such a question, but I hope you'll understand.

Would you mind if I take the majority of your work here and create a port for RethinkDB?

-Aviv

createStrategy does not work

It's asking for...

error: uncaughtException: local authentication strategy requires a verify function

Your example documentation needs updated

Add option to store hash and slat in a seperate collection.

Whenever I fetch a user from the database and send it over the internet I need to make sure that the hash and salt is not sent with the user. I wonder if it could be possible to store the hash and salt in a separate collection that way I know that sensitive information is not retrieved from the database unless I specifically request it.

If this sounds like a good idea I am willing to help.

Question: Authentication "fails" with custom `usernameField`

The question is pretty self explanatory. Basically, I'm having a difficult time debugging whats happening. Maybe taking a look at my code will shed some light.

/* Passport Configuration */
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
/* User model */
var mongoose = require('mongoose'),
    passportLocalMongoose = require('passport-local-mongoose'),
    Schema = mongoose.Schema,
    userSchema = new Schema({

        name: String

    });

userSchema.plugin(passportLocalMongoose, 
{
    usernameField: 'email',
});

User = mongoose.model('user', userSchema);

module.exports = User;
/* Login POST Route */
app.post('/login', passport.authenticate('local'), function(req, res, next){
    res.redirect('/');
});

Setting my route like this ^ results in an unauthorized page. I have also tried:

    app.post('/login', function(req, res, next){
        console.log('logging in...');
        passport.authenticate('local', function(err, user, info){
            console.log(info);

            if(err)
            {
                console.log(err);
                return next(err);
            }

            if(!user)
            {
                return res.redirect('/login');
            }

            req.logIn(user, function(err){
                if(err)
                    return next(err);
                return res.redirect('/');
            });
        })(req, res, next);
    });

Using this method, I've determined that if I POST the email field with name="email", I get a Missing Credentials error. If I post using name="username", the app just hangs eternally. This is all the information I've been able to figure out though. I never hit the err block, user is just always false

Any ideas about what could be happening here @saintedlama? This seems like it should be pretty simple, but I am not making any headway. I'll gladly provide any more info you may need.

usernameField not creating unique index

This may be a problem with the default username field as well, but since I'm using custom to set it to "email," I can't as yet validate the case.

When using mongoose.set('debug', true); I will see in the console:

Mongoose: accounts.ensureIndex({ _id: 1 }) { safe: undefined, background: true, unique: true }

What I expect to see is in the console is:

Mongoose: accounts.ensureIndex({ _id: 1 }) { safe: undefined, background: true, unique: true }
Mongoose: accounts.ensureIndex({ email: 1 }) { safe: undefined, background: true, unique: true }

Because a unique constraint index isn't created on accounts.email, my application allows

// directly in mongodb
db.account.update({ _id: 100 }, { $set: { email: "[email protected]" } })

// from mongoose where account is a document
account.update({ email: "[email protected]" }, function (uerr, numUpdated, raw) { ... });

to go completely unchecked, thus allowing 2+ users with the same username.

I am working around it by specifically defining email in my account schema like this:

...,
email: {
  type: String,
  required: true,
  lowercase: true,
  unique: true
},
...

... but I would expect this to be done already.

Issue getting the last version via npm install

When I'm doing a 'npm install passport-local-mongoose' i'm not getting the last version of the plugin, even if I specify the 0.2.5 version in package.json or in the command line.

Is the npm registry up to date ?

Methods Added to Schema Should Return Promises

Almost all Mongoose methods return promises (using the mpromisemodule), but the methods added by this module don't.

E.g.: User.findOne({name: me}).exec() vs. User.findByUsername(name, callback)

It's not a big deal, just something I noticed. I guess this could be easily added without changing the callback arguments.

user agent spam in terminal

whenever i start the example, without editing at all i get this on every route i open after server start:

127.0.0.1 - - [Sat, 12 Jan 2013 19:14:37 GMT] "GET /favicon.ico HTTP/1.1" 404 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.26 (KHTML, like Gecko) Chrome/26.0.1381.0 Safari/537.26"
127.0.0.1 - - [Sat, 12 Jan 2013 19:14:39 GMT] "GET /js/bootstrap.min.js HTTP/1.1" 304 - "http://127.0.0.1:3000/login" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.26 (KHTML, like Gecko) Chrome/26.0.1381.0 Safari/537.26"
127.0.0.1 - - [Sat, 12 Jan 2013 19:14:39 GMT] "GET /favicon.ico HTTP/1.1" 404 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.26 (KHTML, like Gecko) Chrome/26.0.1381.0 Safari/537.26"
127.0.0.1 - - [Sat, 12 Jan 2013 19:14:41 GMT] "GET /css/bootstrap.css HTTP/1.1" 304 - "http://127.0.0.1:3000/login" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.26 (KHTML, like Gecko) Chrome/26.0.1381.0 Safari/537.26"
127.0.0.1 - - [Sat, 12 Jan 2013 19:14:42 GMT] "GET /favicon.ico HTTP/1.1" 404 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.26 (KHTML, like Gecko) Chrome/26.0.1381.0 Safari/537.26"
127.0.0.1 - - [Sat, 12 Jan 2013 19:14:44 GMT] "GET /css/custom.css HTTP/1.1" 304 - "http://127.0.0.1:3000/login" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.26 (KHTML, like Gecko) Chrome/26.0.1381.0 Safari/537.26"
127.0.0.1 - - [Sat, 12 Jan 2013 19:14:44 GMT] "GET /favicon.ico HTTP/1.1" 404 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.26 (KHTML, like Gecko) Chrome/26.0.1381.0 Safari/537.26"

i would really like to kill that off :)

Option doesn't work

Hi,

I'm a new user of Node.JS and Passport, and I'm trying to use the passport-local-mongoose options to hide all the fields from my MongoDB database that I don't want to show to my users. I'm using the selectFields option but whatever I tried sends me an Unauthorized response. What am I doing wrong?

My code is:

var options = {
selectFields:'token'
};

userSchema.plugin(passportLocalMongoose, options);

Thank you :)

Docs/Examples: Usage as route middleware with success/failure redirects?

None of the examples show the auth strategy used as route middleware using failureRedirect.

I have the following routes on my app. Pretty sure that it's set up correctly, as the post('/login') appears to authenticate, redirecting to '/private'. However private fails to authorize and 302 redirects back to login.

app.get('/private',
passport.authorize('local', {failureRedirect: '/login'}),
function(req, res){
// never gets to here, redirects to login.
res.render('private');
}
);

app.get('/login', function(req, res){
res.render('login-form');
});

app.post('/login', passport.authenticate('local'), function(req, res){
// this code fires after auth.
res.redirect('/private');
})

Allow to populate some fields of the Schema at deserialization time

With options, it is possible to select some fields of the Schema at deserialization, but it is not possible to populate fields at deserialization time.

I think it is possible to modify the findByUsername function like this

schema.statics.findByUsername = function(username, cb) {
    var queryParameters = {};

    // if specified, convert the username to lowercase
    if (options.usernameLowerCase) {
        username = username.toLowerCase();
    }

    queryParameters[options.usernameField] = username;

    var query = this.findOne(queryParameters);
    if (options.selectFields) {
        query.select(options.selectFields);
    }
    if (options.populateFields) {
        query.populate(options.populateFields);
    }

    query.exec(cb);
};

Troubles using "instance.setPassword(pw,cb)" to reset user password

Hi!

I've trying to use this instance method to reset a user password but it never changes the salt & hash in the db.

This is an extract of the code.

Thanks in advance for you help :D and for this plugin, is really saves time.

User.statics.update_profile = function (user_id, new_data, callback) {
    return this.findOne({
        _id: user_id
    }, function (error, doc) {
        if (!error) {
            /*
             * Update password.
             */
            if (new_data.body.new_password == new_data.body.retype_new_password) {
                doc.setPassword(new_data.body.new_password);
                doc.setPassword('new_pass',function(error){
                    console.log(error)
                });
            }
            else {
                console.log('Passwords not match');
            }           
        }
        else {
            callback(error.message);
        }
    });
};

Nested required fields

Hi there,
First of all, thanks for the module. It saved me a lot of time!

My user schema is similar to this (its in coffee, btw):

User = new Schema
  sensitiveData1: String
  sensitiveDate2: Number
  account:
    name: String
    age: Number

User.methods.toJSON = ->
  @toObject().account

This way I'm only sending the account back to the clients, not sending the sensitive data (including password hash and salt).

The thing is that I tried to setup passport-local-mongoose as the following:

User.plugin passportLocalMongoose,
  usernameField: 'account.email'

And it didn't work. A quick look at the source and I found out that the module is always getting the username field like this: user[options.usernameField], which clearly breaks if the field is nested.
The fix if fairly simple, at a first glance, not sure of its side effects, though. I'd be happy to fix it (if this is a feature that you want to support) when I have time... Just wanted to capture it here.

Cheers!
-g

How do I use options? (it's in the readme, sorry)

Given something like this:

var mongoose = require('mongoose')
    , Schema = mongoose.Schema
    , passportLocalMongoose = require('passport-local-mongoose');

var Account = new Schema({
    type: { type: String, default: 'user' },
    displayname: String
});

Account.plugin(passportLocalMongoose);

module.exports = mongoose.model('Account', Account);

Where do you configure passport-local-mongoose?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.