How to Hash Passwords in NestJS

Updated: January 1, 2024 By: Guest Contributor Post a comment

Introduction

When building web applications, securing user credentials is vital. In this tutorial, you will learn how to implement secure password hashing in a NestJS application using modern techniques and libraries.

Getting Started

To begin with, ensure you have a NestJS project setup. If not, create one using the following command:

nest new your-project-name

Once your project is ready, navigate to the project directory:

cd your-project-name

Installing Necessary Libraries

You will need to install bcrypt or Argon2, libraries that help in hashing passwords. Argon2 is considered very secure but can be more complex to install due to additional system dependencies, while bcrypt is widely used and easier to install:

For bcrypt:

npm install bcrypt

For Argon2:

npm install argon2

Basic Password Hashing with Bcrypt

The bcrypt algorithm is a widely used method for password hashing. Here’s how to use it in NestJS.

import * as bcrypt from 'bcrypt';

async function hashPassword(password: string): Promise {
    const salt = await bcrypt.genSalt();
    return await bcrypt.hash(password, salt);
}

Call this function whenever you need to hash a password, for instance, in the user registration process.

Using Bcrypt in NestJS Services

Integrate bcrypt into a NestJS service as follows:

import { Injectable } from '@nestjs/common';
import * as bcrypt from 'bcrypt';

@Injectable()
export class AuthService {
    async hashPassword(password: string): Promise {
        const salt = await bcrypt.genSalt();
        return await bcrypt.hash(password, salt);
    }
}

You can now inject this service into controllers where user registration occurs.

Advanced Password Hashing with Argon2

Argon2 is the winner of the Password Hashing Competition and is recommended for new applications. Here is how you can use it:

import * as argon2 from 'argon2';

async function hashPassword(password: string): Promise {
    return await argon2.hash(password);
}

Using Argon2 in NestJS with Dependency Injection

To properly utilize Argon2 in your NestJS app, create a service that uses dependency injection.

import { Injectable } from '@nestjs/common';
import * as argon2 from 'argon2';

@Injectable()
export class AuthService {
    async hashPassword(password: string): Promise {
        return argon2.hash(password);
    }
}

Just like with bcrypt, you will inject this AuthService into your registration controller.

Validating Passwords

Here’s how to verify that a user-entered password matches the hashed version stored in your database.

Bcrypt

async function validatePassword(userPassword: string, storedHashPassword: string): Promise {
    return bcrypt.compare(userPassword, storedHashPassword);
}

Argon2

async function validatePassword(userPassword: string, storedHashedPassword: string): Promise {
    return argon2.verify(storedHashedPassword, userPassword);
}

These functions can be included in your AuthService and used during login flow.

Handling Hashing Securely

To enhance the security aspect of hashing, consider the following best practices:

  • Never hash passwords synchronously.
  • Use a salt to make each hash unique.
  • Consider using a pepper (an additional secret added to the hash process).
  • Store hashes securely in your database.

Integrating Password Hashing in User Workflow

Let’s integrate password hashing in user sign-up and login processes in your NestJS application.

Registration Process

Demonstrate how to create a user signup method that hashes the password:

@PostMapping('/signup')
public async signup(@Body() body: { password: string }) {
    const hashedPassword = await this.authService.hashPassword(body.password);
    // Persisting the hashed password to the database goes here.
}

Login Process

Show an example method of a user login process that validates the password:

@PostMapping('/login')
public async login(@Body() body: { username: string, password: string }) {
    // Fetching the user and hashed password from the database goes here.
    const isPasswordValid = await this.authService.validatePassword(body.password, user.hashedPassword);
    if (!isPasswordValid) {
        throw new UnauthorizedException('Invalid credentials');
    }
    // Proceed with login process (e.g., token generation)
}

Conclusion

Effective password hashing is critical for the security of user data. By following the steps outlined in this guide and by adhering to best practices, you’ll enhance your NestJS application’s security, prevent common vulnerabilities and protect your users. Always stay updated with the latest cryptographic practices and NestJS updates.