Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

passport-local-mongoose

saintedlama69.5kMIT8.0.0TypeScript support: included

Mongoose plugin that simplifies building username and password login with Passport

mongoose, passport, authentication, login

readme

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.

changelog

Changelog

All notable changes to this project will be documented in this file. See standard-version for commit guidelines.

8.0.0 (2023-03-14)

⚠ BREAKING CHANGES

  • Upgraded to mongoose 7 and removed node 12 support

Features

7.1.2 (2022-05-21)

7.1.1 (2022-05-17)

Bug Fixes

  • add usernameCaseInsensitive to the typings (#355) (26f4991)

7.1.0 (2022-05-17)

Features

7.0.0 (2022-03-13)

Features

  • reset maxAttempts after unlockInterval if activated (#349) (a752854)

6.3.0 (2022-03-12)

Features

  • reset maxAttempts after unlockInterval if activated (#349) (a752854)

6.2.2 (2022-03-12)

6.2.1 (2022-03-12)

6.2.0 (2022-03-12)

Features

  • escape username regex (#296) (6713c4e)
  • upgrade dependencies and drop support of legacy mongodb and node versions (#344) (2d88e70)

6.1.0 (2021-01-09)

Features

  • remove babel and semver checks for crypto parameters (857f93a)

6.0.1 (2020-01-04)

Bug Fixes

  • remove superfluous files from package (b1681a5)

6.0.0 (2020-01-04)

⚠ BREAKING CHANGES

  • mongodb driver update requires server port to be specified

Bug Fixes

  • make debug a dev dependency (c9b0a78)
  • update dependencies and specify server port in tests (807d9cf)
  • use Buffer.from instead of new Buffer (37375b8)

5.0.1 (2018-06-20)

Bug Fixes

  • require node engine >= 6.0.0 (b18f083)
  • update nyc dev dependency (74854b1)

5.0.0 (2018-03-01)

Bug Fixes

  • add back passwordValidatorPromisified (4794266)
  • promise return signature (0612b5b)
  • update build matrix (788e0c9)

Features

  • implement promises for authenticate and static authenticate including tests (6752e0c)
  • promisify external functions not part of passport.js interface (e687753)

BREAKING CHANGES

  • drop support for node 4 and 5

4.4.0 / 2017-10-25

  • 4.4.0
  • Merge pull request #233 from 4umfreak/master Issue #79 and Bug #58, handle save() asynchronously
  • Update changelog

4.3.0 / 2017-10-25

  • 4.3.0
  • Merge pull request #234 from MeestorHok/master Fixed vulnerable dependency
  • Fixed vulnerable dependency
  • fixed up code tabbing style differences
  • added code and tests to handle mongoose errors and concurrency gracefully.

4.2.1 / 2017-08-26

  • 4.2.1
  • Revert setting hash and salt to null in model since this is a breaking change with possibly the implication to loos credentials in a running system
  • Remove superfluous parameters and ;

4.2.0 / 2017-08-24

  • 4.2.0
  • Remove methuselah aged node.js versions 0.10 and 0.12 from travis build matrix
  • Correct test to check that salt and hash are null
  • Merge branch 'master' of github.com:saintedlama/passport-local-mongoose
  • Implement findByUsername option. Fixes #227
  • Move function setPasswordAndAuthenticate to end of file
  • Merge pull request #226 from guoyunhe/patch-1 Hide hash and salt fields of user in register()
  • Change undefined to null
  • Hide hash and salt of user in authenticate callback After authentication, salt and hash are usually not used anymore. It is better to drop them to avoid exposing in req.user
  • Hide hash and salt fields of user in register() Usually, in register() callback, you do not need salt and hash anymore. They should be hidden to avoid exposing to API.

4.1.0 / 2017-08-08

  • 4.1.0
  • Move to nyc for coverage
  • Adapt change password functionality and tests
  • Refactor authenticate function to its own module
  • Merge pull request #128 from Gentlee/change-password Implement changePassword method #127
  • Merge pull request #140 from AshfordN/patch-2 Update index.js
  • Add syntax highlighting to code examples
  • Modernize example code by using single line variable declarations and const
  • Refactor pbkdf2 adapter to a module of its own
  • Update dependencies
  • Update build matrix to test against node 7, 8 and mongodb 3.4
  • Compare fields and not object to avoid fields added by mongoose to break the build
  • Downgrade to cross-env ^2.0.0.0 to run tests on node 0.10 and 0.12
  • Update dependencies and adapt code to pass buffers to scmp 2
  • Set timeout to 5000ms for all tests
  • Use the ^ semver operator instead of 4.5.x operator
  • Update dependencies and add debug dependency
  • Minor code style fixes
  • Migrate from assert to chai.expect
  • Retry dropping mongodb collections Implementation works around a mongoose issue that background indexes are created while trying to drop a collection
  • Migrate to chai.expect
  • Migrate to chai.expect and cleanup code
  • Rename test "error" to "errors" to match tested file
  • Update index.js Corrected Grammatical error in the IncorrectUsernameError and IncorrectUsernameError messages
  • Simplify .travis.yml by moving dependencies required for coverage to dev dependencies
  • Adapt .travis.yml to new container based infrastructure
  • Fix output handling in shelljs 0.7
  • Use cross-env for cross platform tests
  • if user model doesn't include salt/hash, get them from db, change tests timeouts
  • optimize and add test for situation when passwords are the same
  • fix changePassword() test
  • implement changePassword method
  • Merge pull request #123 from Gentlee/optimize-lowercase optimize username lowercasing
  • Remove io.js from build matrix
  • Use travis container-based infrastructure
  • Simplify repository field
  • Use digestAlgorithm sha1 and sha1 generated hash for backward compatibility tests
  • optimize username lowercase
  • Add test to verify that authenticate/hashing is 3.0.0 compatible

4.0.0 / 2016-01-15

  • 4.0.0
  • Revert "Revert "Use semver to do a version check instead of argument length checks"" This reverts commit e17e720867eb283789d9461ec9b452fb513ee52e.

3.1.2 / 2016-01-15

  • 3.1.2
  • Revert "Use semver to do a version check instead of argument length checks" This reverts commit 8732239272636272badcc7e88e0483fdd2be0366.

3.1.1 / 2016-01-15

  • 3.1.1
  • Run tests against latest 4.x and latest 5.x versions
  • Use semver to do a version check instead of argument length checks
  • Update changelog

3.1.0 / 2015-10-05

  • 3.1.0
  • Bring back customizable error messages

3.0.0 / 2015-09-21

  • 3.0.0
  • Make the example depend on the latest npm version
  • Move main file to index.js to simplify the package
  • Refactor error generation and yielding
  • Rename variable Err to errors
  • Move mongotest module to helpers
  • Merge pull request #105 from opencharterhub/fix/error-handling Error handling: Always return instance of 'AuthenticationError'
  • Lint: Add some semicolons
  • Lint: Handle error case
  • Lint: Don't shadow variable names
  • Error handling: Always return instance of 'AuthenticationError'

2.0.0 / 2015-09-14

  • 2.0.0
  • Update changelog
  • Add upgrade warning and document new default digest algorithm
  • Add node.js 4.0.0 as build target
  • Reformat code
  • Add editorconfig
  • Update dependencies

1.3.0 / 2015-09-14

  • 1.3.0
  • Remove superfluous queryParameters declaration
  • Add missing semicolon
  • Merge pull request #98 from theanimal666/master Fix Issue #96
  • Replace my tabs with spaces to macth project coding style
  • Support test MongoDB server other then localhost Implemented using MONGO_SERVER environment variable
  • Merge remote-tracking branch 'upstream/master'
  • Make authenticate work without salt/hash selected by default
  • Add a generated changelog

1.2.0 / 2015-08-28

  • 1.2.0