Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript User Authentication With Express and Mongo Sessions and Cookies Creating the Profile Route and Page

Tennyson Horn
Tennyson Horn
14,660 Points

[SOLVED]: "401 Error: 'Wrong email or password'" on login attempt

SOLUTION: pretty simple mistake. Took a while to realize. In the bcrypt.compare() function call I had the callback function arguments in reverse order from the docs . . . i.e.:

bcrypt.compare(password, user.password, function(result, error){ . . .

correcting to . . .

bcrypt.compare(password, user.password, function(error, result){ . . .

fixed the problem stated below.

ORIGINAL QUESTION:

Everything works so far, except the login. When I signup a user, it writes to the db, creates a session and redirects to the profile page with the user info filled in. However, if I try to log in, I get bounced to the error page with the "Wrong email or password" message.

I've been messing around using console.log. The app is receiving the form data correctly, and performing the db lookup correctly. I'm digging into the bcrypt.compare() method now to try and see what's up. Best idea I have so far is that it's comparing the plain text password entry to the password hash in the db rather than hashing the entry and comparing it to the hash in the db.

Anyone else have any ideas?

for reference, here's my code...

index.js:

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

// GET /profile
router.get('/profile', function(req, res, next) {
  if (!req.session.userId) {
    //for sneaky folks who try to access user pages w/out being logged in
    const err = new Error("I can't let you do that, friend.");
    err.status = 403;
    return next(err);
  }
  User.findById(req.session.userId)
    .exec(function(error, user){
      if (error) {
        return next(error);
      } else {
        return res.render('profile', {title: 'Profile', name: user.name, favorite: user.favoriteBook})
      }
    });
});

// GET /login
router.get('/login', function(req, res, next){
  return res.render('login', {title: 'Log In'});
});

// POST /login
router.post('/login', function(req, res, next){
  if (req.body.email && req.body.password) {
    //call the authenticate method from user.js
    User.authenticate(req.body.email, req.body.password, function(error, user) {
      //if passwords dont match, throw wrong credentials error
      if (error || !user) {
        const err = new Error('Wrong email or password.');
        err.status = 401;
        return next(err);
      } else {
        //if everything matches, create session for user and redirect to profile splash page
        req.session.userId = user._id;
        return res.redirect('/profile');
      }
    });
    //throw error if missing one or both credentials
  } else {
    const err = new Error('Email and password are required');
    err.status = 400;
    return next(err);
  }
});

// GET /register
router.get('/register', function(req, res, next) {
  return res.render('register', { title: 'Sign Up' });
});

// POST /register
router.post('/register', function(req, res, next) {
  if (req.body.email &&
      req.body.name &&
      req.body.favoriteBook &&
      req.body.password &&
      req.body.confirmPassword) {

     //make sure password and confirmation actually match
    if (req.body.password !== req.body.confirmPassword) {
      const err = new Error('Passwords do not match');
      err.status = 400;
      return next(err);
    }

    //Creat an object w/ the form data
    const userData = {
      email: req.body.email,
      name: req.body.name,
      favoriteBook: req.body.favoriteBook,
      password: req.body.password
    };

    //Use the mongoose schema's 'create' method to insert the object into mongo
    //this passes the userData to our user.js file for validation, password hashing, and writing to db
    User.create(userData, function (error, user) {
      if (error) {
        return next(error);
      } else {
        req.session.userId = user._id;
        return res.redirect('/profile');
      }
    });


  } else {
    const err = new Error('All fields are required.');
    err.status = 400;
    return next(err);
  }
});

// GET /
router.get('/', function(req, res, next) {
  return res.render('index', { title: 'Home' });
});

// GET /about
router.get('/about', function(req, res, next) {
  return res.render('about', { title: 'About' });
});

// GET /contact
router.get('/contact', function(req, res, next) {
  return res.render('contact', { title: 'Contact' });
});

module.exports = router; //export the router object to it can be used appropriately in app.js

And, user.js:

const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const UserSchema = new mongoose.Schema({ //create a schema for our user data
      email: {
        type: String, //define typof
        unique: true, //must not match any other entrys in the users collection
        required: true, //is required for submission to the db
        trim: true //trim off leading and trailing spaces, if present
      },
      name: {
        type: String,
        required: true,
        trim: true
      },
      favoriteBook: {
        type: String,
        required: true,
        trim: true
      },
      password: {
        type: String,
        required: true
      }
});

//authenticate the user login credentials
UserSchema.statics.authenticate = function(email, password, callback) {
  User.findOne({email: email})
    .exec(function(error, user) {
      if (error) {
        return callback(error);
      } else if (!user){
        //if db is queried and no match found alert the user
        const err = new Error('User not found.');
        err.status = 401;
        return callback(err);
      }
      // compare the provided password to the queried user's hash from their db document
      bcrypt.compare(password, user.password, function(result, error) {
        if (result === true) {
          //if match, return null in place of error and supply the user's info from the db
          return callback(null, user);
        } else {
          return callback();
        }
      });
    });

};

//hash the password before sending it to the db
UserSchema.pre('save', function(next) {
  const user = this;
  bcrypt.hash(user.password, 10, function(err, hash){ //define what to hash, how many times to hash it
     if (err) {
       return next(err);
     }
     user.password = hash; //overwrite the plain text password with the hashed version
     next();
  });
});

const User = mongoose.model('User', UserSchema); //make the schema a model and assign it to a variable
module.exports = User; //make the model available as the export