TripleTen User Schema Finduserbycridentials

PHOTO EMBED

Wed Apr 03 2024 03:35:59 GMT+0000 (Coordinated Universal Time)

Saved by @Marcelluki

The promise chain works like this:

We check if a user with the submitted email exists in the database.
If the user is found, the hash of the user's password is checked.
In this lesson, we'll improve our code. We'll actually make the code for checking emails and passwords part of the User schema itself. For this, we'll write a method called findUserByCredentials(), which has two parameters, email and password, and returns either a user object or an error.

Mongoose allows us to do this, and in order to add a custom method, we'll need to set it on the statics property on the desired schema:

// models/user.js

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    unique: true
  },
  password: {
    type: String,
    required: true,
    minlength: 8
  }
});
// we're adding the findUserByCredentials methods to the User schema 
// it will have two parameters, email and password
userSchema.statics.findUserByCredentials = function findUserByCredentials (email, password) {

};

module.exports = mongoose.model('user', userSchema);
 Save
All that's left is to write this method's code. In the future, we'll use the method like so:

User.findUserByCredentials('elisebouer@tripleten.com', 'EliseBouer1989')
  .then(user => {
        // we get the user object if the email and password match
  })
  .catch(err => {
        // otherwise, we get an error
  });
 Save
The findUserByCredentials() Method
In order to find a user by email, we'll need the findOne() method, which takes email as an argument. The findOne() method belongs to the User model, so we'll call this method using the this keyword:

// models/user.js

userSchema.statics.findUserByCredentials = function findUserByCredentials (email, password) {
  // trying to find the user by email
  return this.findOne({ email }) // this — the User model
    .then((user) => {
      // not found - rejecting the promise
      if (!user) {
        return Promise.reject(new Error('Incorrect email or password'));
      }

      // found - comparing hashes
      return bcrypt.compare(password, user.password);
    });
};

module.exports = mongoose.model('user', userSchema);
 Save
The findUserByCredentials() method should not be an arrow function. This is so that we can use this. Otherwise, the value of this would be statically set, as arrow functions remember the value of this from their initial declaration.

Now we need to add an error handler for whenever the hashes don't match. We'll write the code for this in a further then() function.

But first, let's take a quick look at how we should NOT do this:

// models/user.js

userSchema.statics.findUserByCredentials = function findUserByCredentials (email, password) {
  // trying to find the user by email
  return this.findOne({ email })
    .then((user) => {
      // not found - rejecting the promsie
      if (!user) {
        return Promise.reject(new Error('Incorrect email or password'));
      }

      // found - comparing hashes
      return bcrypt.compare(password, user.password);
    })
    .then((matched) => {
      if (!matched) // rejecting the promise
      
      return user; // oh - the user variable is not in this scope
    });
};

module.exports = mongoose.model('user', userSchema);
 Save
In the second then(), we're returning a user object which doesn't exist in that scope as it was left back in the previous then().

In order to solve this problem, we should organize our promise chain differently. Let's add a then() handler to bcrypt.compare():

// models/user.js

const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const { Schema } = mongoose;

const userSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    unique: true
  },
  password: {
    type: String,
    required: true,
    minlength: 8
  }
});

userSchema.statics.findUserByCredentials = function findUserByCredentials (email, password) {
  return this.findOne({ email })
    .then((user) => {
      if (!user) {
        return Promise.reject(new Error('Incorrect email or password'));
      }

      return bcrypt.compare(password, user.password)
        .then((matched) => {
          if (!matched) {
            return Promise.reject(new Error('Incorrect email or password'));
          }

          return user; // now user is available
        });
    });
};

module.exports = mongoose.model('user', userSchema);
 Save
The method is ready. Now we can apply it to the authentication handler:

// controllers/users.js

module.exports.login = (req, res) => {
  const { email, password } = req.body;

  return User.findUserByCredentials(email, password)
    .then((user) => {
            // authentication successful! user is in the user variable
    })
    .catch((err) => {
            // authentication error
      res
        .status(401)
        .send({ message: err.message });
    });
};
content_copyCOPY

https://tripleten.com/trainer/web/lesson/16d932f2-afa2-402e-baec-5a1c4cde611e/