Coding Journal

Jordan Vidrine

Daily Coding Journal

An experienced business owner + musician with a demonstrated history of management in the Energy sector and an insatiable appetite for learning new and complex skills. Transitioning from an ownership role & seeking employment in Web Development as a Jr Full Stack Developer.

July 3rd 2019

Spent the first couple of hours of my day trying to integrate pagination into this blog. It turned out very frustrating and uneventful because github pages does not support the newest jekyll pagination gem. I couldn’t for the life of me get the v1 of their gem to work either. I’ll have to look into that in some free time. Onto my node course!

User Password Hashing using bcrpyt

We will use bcrpyt in our task-manager app (the one I’ve been creating in this course) to save hashed versions of user passwords, as opposed to the literal password string. The following code will use bcrpyt.hash() to do this.

const bcrpyt = require('bcryptjs')

const myFunction = async () => {
  const password = 'Red12345!'
// hash() accepts the string to hash, and a number of rounds to run the hash
// algorithm. 8 is the value recommended from the original creator of the
// bcrypt algorithm
  const hashedPassword = await bcrypt.hash(password, 8)
  console.log(password)
  console.log(hashedPassword)


}

myFunction()

Hashing algorithms are one way algorithms. You cannot reverse this, by design, to keep passwords safe and secure.

How can a user login if we are saving the passoword as a hash? bcrpyt gives us a way to do that. We can hash the password the user enters and compare it to the hashed password stored in the database. The method we will use for this is bcrypt.compare(passwordString, hashedPasswordToCompareItTo)

Implementing in a mongoose model with middleware

Middleware are functions which are passed during the execution of async functions. For documents, the following doccument functions support middleware : validate, save, remove, + init. We want to run some code, BEFORE, a user is saved.

To do this, instead of passing an object to the User created with mongoose.model() , we will predefine a schema to pass in. This separates the creation of the schema from the creation of the model like so.

const userSchema = new mongoose.Schema({
// data here
})

// middleware to run BEFORE saving
userSchema.pre('save', async function (next) {
  const user = this;
  console.log('just before saving')
  // needs to be called or else the middleware wont move forward in the save process
  next()
})

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

module.exports = User

Now, if we want to get this to work when UPDATING, and not only when creating a new user, we have some changes to make in our router file because certain mongoose queries bypass middleware.

In the try block of our user update put method in the user routers, we make this change:

const user = await User.findById(req.params.id)
updates.forEach((updateField) => {
  user[updateField] = req.body[updateField]
})
// old way of doing it
// const user = await User.findByIdAndUpdate(req.params.id, req.body, {new: true, runValidators: true})

Now all that’s left is to write the middleware to look for a change or a save to ‘password’ and run code when that happens. Inside of the userSchema middleware for ‘save’ we add:

const user = this;

if (user.isModified('password')) {
  user.password = await bcrypt.hash(user.password,8)
}

next()

Logging a user in

The first thing we need to do is add a post route for a user to send their email and password to the DB to check if those things are correct. We will create a method on the User model to do this. We can do this in the User model js file after we define the user like so:

// adds the method to the User model
userSchema.statics.findByCredentials = async (email, password) => {
  user = await User.findOne({ email })
  if (!user) {
    throw new Error('Unable to login')
  }

  const isMatch = await bcrypt.compare(password, user.password)

  if (!isMatch) {
    throw new Error('Unable to login')
  }
  return user
}

We will call that function from our route like so, and ALSO add the ‘unique:true’ option to our Email area in the UserSchema, not allowing multiple users to have the same email

//get user from Email
router.post('/users/login', async (req,res) => {
  try {
    const user = await User.findByCredentials(req.body.email,req.body.password)
    res.send(user)
  } catch(e) {
    res.status(400).send()
  }
})

Setting up authentication

We want to have most of the routes be private. We do not want people who are not logged in to be able to create tasks and edit tasks, especially ones not their own. The only public routes should be logging in and creating a new user.

To do this, we need to setup the login request to send back an authentication token. It will allow the user to use that token to accomplish tasks that need to be authenticated. We will use the JSON web token (JWT) to do this.

Stopped my learning with this last lesson. I finished the day reading Eloquent Javascript chapters on Modules and Asynchronus programming. Going to re - read the async portion and work through the exercises.

Back to blog...