Introduction: The Importance of Secure User Registration

Welcome to the first lesson of the Secure Your API with Authentication and Authorization course. In this lesson, you’ll implement secure user registration in your NestJS API.

Why is this important?

Applications that accept user accounts need to store passwords securely. Storing passwords in plain text is extremely risky. If your database is ever exposed, attackers gain direct access to all accounts.

To avoid this, we use password hashing before saving user credentials.

By the end of this lesson, you’ll have a working /auth/register endpoint that:

  • Accepts a name, username, and password.
  • Hashes the password securely before storing.
  • Saves the new user in a mock database.
  • Rejects duplicate usernames.

You’ll also get a first look at JWTs (JSON Web Tokens), which will power authentication in later lessons.

Understanding Password Hashing

Hashing is a one-way transformation of a password into a fixed string of random-looking characters.

  • It cannot be reversed back into the original password.
  • It uses a salt (a random value) to ensure that even two identical passwords result in different hashes.
  • It applies multiple iterations of hashing to make brute-force attacks slower.

How Do We Use It in This Project?

In our project, we use bcrypt, a library specifically designed for password hashing. The logic lives in src/utils/auth.utils.ts:

const BCRYPT_ROUNDS = 10; // reasonable default for demos

export function hashPassword(password: string): string {
  const salt = bcrypt.genSaltSync(BCRYPT_ROUNDS);
  return bcrypt.hashSync(password, salt);
}

export function verifyPassword(password: string, passwordHash: string): boolean {
  try {
    return bcrypt.compareSync(password, passwordHash);
  } catch {
    return false;
  }
}
  • bcrypt.genSaltSync(10) generates a random salt with 10 “rounds” (computational cost factor).
  • bcrypt.hashSync(password, salt) produces the stored password hash.
  • Both the salt and the hash are embedded in the stored string.

When verifying a login attempt, bcrypt.compareSync:

  • Extracts the salt from the stored hash.
  • Re-hashes the provided password with that salt.
  • Compares the result to the stored hash. If they match → the password is correct.

Why Not Store Plain Passwords? Imagine two users register with the password password123:

  • Plain text: both DB rows show the same string.
  • With bcrypt: each row looks different, because each hash uses a unique salt. This ensures stronger protection against database leaks and precomputed attack tables.

Mock DB vs Real DB

  • In our mock DB (mock-db.ts), new users are written back into a TypeScript file using the persistUserSeed function.
  • In a real database (like Postgres or MongoDB), the hashed password would simply be stored in a passwordHash column.
Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal