Table of Contents
- Introduction
- Getting Started
- Installing Necessary Libraries
- Basic Password Hashing with Bcrypt
- Using Bcrypt in NestJS Services
- Advanced Password Hashing with Argon2
- Using Argon2 in NestJS with Dependency Injection
- Validating Passwords
- Handling Hashing Securely
- Integrating Password Hashing in User Workflow
- Conclusion
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.