Part 130: Securing User Passwords with Bcrypt: A Step-by-Step Guide

[App] Authentication User Database

[App] Authentication User Database

In the world of web development, securing user data is paramount, especially when it comes to passwords. In our previous discussion, we implemented an authenticateUser function to verify login credentials against a database. However, storing passwords as plain text poses a significant security risk. In this post, we'll explore how to enhance the security of stored passwords using the Bcrypt library.

Why Encrypt Passwords?

Storing passwords in plain text is dangerous. If unauthorized individuals gain access to your database, they can view and misuse these passwords. This risk is amplified if users reuse passwords across multiple sites. To mitigate this, we encrypt passwords using one-way hashing algorithms, making it nearly impossible to derive the original password from the stored hash.

Introducing Bcrypt

Bcrypt is a popular and secure hashing algorithm that provides one-way encryption. It includes a "salt" to ensure that even identical passwords produce unique hashes. This means even if two users have the same password, their stored hashes will differ, adding an extra layer of security.

Updating Our Project

1. Modify the Database Schema

First, we need to update our database schema to store password hashes instead of plain text passwords. We'll rename the password field to passwordHash in our Prisma schema.

// File path: prisma/schema.prisma

model User {
  id           String   @id @default(uuid())
  email        String   @unique
  name         String
  passwordHash String
}

After modifying the schema, run the prisma db push command to sync these changes with the database. Note that this will clear existing data if no default values are provided, which is acceptable during local development but requires caution in production environments.

//update db
npx prisma db push

2. Install Bcrypt

To use Bcrypt in our project, add it to your dependencies by running:

npm install bcrypt

3. Update the createUser Function

Next, we'll modify the createUser function to hash passwords before storing them in the database.

// File path: lib/users.js

import { hash } from 'bcrypt';
import { db } from './db';

export async function createUser({ email, name, password }) {
  const passwordHash = await hash(password, 10); // 10 rounds for salt generation
  return await db.user.create({
    data: { email, name, passwordHash },
  });
}

In this function, we use Bcrypt's hash method to encrypt the password, generating a passwordHash which is then stored in the database.

4. Update the authenticateUser Function

For password verification during sign in, we need to update the authenticateUser function. This involves loading the user based on their email and comparing the entered password with the stored hash.

// File path: lib/users.js

import { compare } from 'bcrypt';
import { db } from './db';

export async function authenticateUser(email, password) {
  const user = await db.user.findUnique({
    where: { email },
  });
  if (user && await compare(password, user.passwordHash)) {
    return user;
  }
}

Here, Bcrypt's compare function checks if the entered password matches the stored hash, ensuring secure authentication.

Testing the Changes

To verify our updates, create a new user and check the database to confirm that the password is now stored as a hash. When signing in, the system should authenticate using the hashed password, maintaining functionality while enhancing security.

Conclusion

By incorporating Bcrypt into our project, we've significantly improved password security. Hashing passwords before storage protects users even if database security is compromised. For further reading, explore more about Bcrypt on Wikipedia or its npm package documentation. Remember, securing user data is not just a technical requirement—it's a responsibility.

Last updated