Hashing and Verifying Passwords in Mongoose Schema
Tags: node, javascript, typescript, webdev
Published: 2023-11-16 09:22:25

Are you creating a web application that is going to require users to sign in with passwords? It's a good security measure to store passwords in the database as hashed strings instead of plain strings. Hashing passwords is done to protect users' passwords. In case an attacker gains access to your database, they will see the hashed passwords but they won't be able to use them. 

In this article, we will learn how to create a user schema that hashes users' passwords before saving them to the database. We will also learn how to add an instance method to the schema that verifies user passwords during login.


We will not cover other surrounding topics such as user authentication and user registration middleware in this article. The main goal of this article is to show you how to create a self-contained schema. This schema will make it easy for you to implement user registration and authentication middleware without worrying about password hashing.

We will use Typescript in this article. This article does not go into depth about how to create models and schema with Mongoose and Typescript. Therefore, this article assumes that you know how to create Mongoose schema using Typescript. If you are new to using Typescript, visit this [article on creating Mongoose schema with Typescript](https://dev.to/ghostaram/how-to-create-mongoose-models-using-typescript-7hf) before continuing.

Before we dive into coding, let us list out the procedure we are going to follow.
1. Initialize a working directory and configure Typescript.
2. Create the user schema.
3. Implement password hashing.
4. Implement password Verification.

Let us start by creating a working directory.

## 1. Initializing working directory and configure Typescript.
In this step, we will [clone a pre-configured working directory from GitHub](https://github.com/GHOST-Aram/node-ts-starter). You can also create your own directory with configurations of your choice. If you need help, see [how to set up an initial project directory for Node and Typescript]( (https://dev.to/ghostaram/initializing-project-directory-for-node-and-typescript-1f8e)).

Your working directory should have something looking like this.

![Initial working directory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/epwug6e69rwyt1307nb1.png)


After successfully cloning the repository, install its dependencies using the following command:

```
npm install
```

With the working directory set up, let us create the user schema.

## 2. Creating the user schema.
To create a Mongoose schema, we need to install `mongoose` and import it to our models file. 

Install mongoose using the following command:

```
npm install mongoose
```

Create the `User.model.ts` file in the `src` directory of your project and import Mongoose.

```
import mongoose from "mongoose"
```

Create an interface for the raw document as created in the code snippet below.

```
interface IUser{
    first_name: string,
    last_name: string,
    password: string,
    username: string
}
```

Create the user schema that implements the interface as created in the code snippet below.

```
const userSchema = new mongoose.Schema<IUser>({
    first_name: String,
    last_name: String,
    password: String,
    username: String
})
```

Now we have successfully created a user schema, let's add password hashing logic in step 3.

## 3. Implement password hashing.
In the last step, we implemented a simple user schema. Here, we will add a functionality that will hash every password entered by the user before saving. 

To add a logic that is executed before saving a document, we use the `pre` hook of the Mongoose `Schema`.

We will use the `bcrypt` module to hash user passwords. Install the `bcrypt` module and its types using the following commands.

```
npm install bcrypt
npm install -D @types/bcrypt
```

Import the `hash` function from `bcrypt` as follows.

```
import { hash } from 'bcrypt'
```
The following code snippet shows how the password hashing on save can be implemented on the schema.

```

userSchema.pre('save', async function(next){

    const hashedPassword =  await hash(this.password, 10)
    this.password = hashedPassword

    next()
})
```

The above code snippet does the following:  
- Convert the raw password into a hash string. 
- Replace the original raw password with the hashed password.
- Call the  `next` callback function. The callback signals Mongoose that we are done hashing the password and it can now be saved to the database.

At this point, we are done with the password hashing functionality. Let us add an instance method to verify passwords in the next step.

## 4. Implement password verification.
In the previous section, we added password hashing to our schema. In this section, we will add a password verification method to the user schema. 

Before adding the method to the schema, we need to create an interface for instance methods. The following code snippet shows the declaration of the interface with the signature for the verification function.

```
interface UserMethods{
    isValidPassword:(password: string) => Promise<boolean>
}
```

Next, we need to add the interface to the `Schema` constructor and `Model` type as follows.

```
type UserModel = Model<IUser,{}, UserMethods>

const userSchema = new mongoose.Schema<IUser, UserModel, UserMethods>({
    first_name: String,
    last_name: String,
    password: String,
    username: String
})
```

Finally, we implement the `isValidPassword` method as follows:

Import the `compare` function and implement the method.

```
import { compare } from 'bcrypt'
```

```
userSchema.method('isValidPassword', async function(
    password: string): Promise<boolean>{
    const isValid = await compare(password, this.password)
    return isValid
})
```

Export the model as follows.

```
export const User = model<IUser, UserModel>('User', userSchema)
```
This method will be accessible through the user document. In the implementation of authentication, this method can be called on the user document. The method returns a promise. The promise resolves to true or false depending on whether the user entered the correct password or not.


## Conclusion
Finally, we have learned how to create a self-contained user schema. The schema can convert a password into a hash string before saving it to the database. The schema can also verify user passwords against the stored hashed passwords during authentication. Visit [this article](https://dev.to/ghostaram/how-to-create-mongoose-models-using-typescript-7hf) to learn more about creating Mongoose models with Typescript.