🔐 User Signup with Password

User registration is the first interaction many users have with your API. In this lesson, we'll implement secure user signup with password hashing, understand why we hash passwords, and explore email verification strategies used in production applications.


The Signup Strategy

<aside> 🎯

Registration Flow:

  1. Validate Input: Check email format, password strength
  2. Check Uniqueness: Ensure email/username isn't taken
  3. Hash Password: Never store plain text passwords
  4. Create User: Save to database
  5. Generate Token: Auto-login after signup
  6. Send Welcome Email: (Optional) Verify email ownership </aside>

Why Hash Passwords?

Storing passwords in plain text is one of the worst security mistakes you can make. Here's why hashing is critical:

Without Hashing 🚫

-- Database breach exposes:
id | email | password
1  | [[email protected]](<mailto:[email protected]>) | MyPassword123!
2  | [[email protected]](<mailto:[email protected]>) | admin2024

Consequences:

With Hashing ✅

-- Database breach exposes:
id | email | password
1  | [[email protected]](<mailto:[email protected]>) | $2b$12$N9qo8uL...
2  | [[email protected]](<mailto:[email protected]>) | $2b$12$K7xI9pQ...

Protection:

<aside> ⚠️

Real World Breaches:

Don't be the next headline - always hash passwords!

</aside>


How Password Hashing Works

Bcrypt: The Gold Standard

Bcrypt is specifically designed for password hashing with built-in protection:

// Input password
"MyPassword123!"

// Bcrypt process:
1. Generate random salt: "$2b$12$N9qo8uLOickgx2ZMRZoMye"
2. Combine password + salt
3. Apply blowfish algorithm 2^12 times (4,096 iterations)
4. Output hash: "$2b$12$N9qo8uLOickgx2ZMRZoMye.IjQ0JwF2cJX8nnYA5VYA.KQIeqHLWa"

Implementing User Registration

The Register Controller

<aside> 📝

File: src/controllers/authController.ts

</aside>

import type { Request, Response } from 'express'
import bcrypt from 'bcrypt'
import { generateToken } from '../utils/jwt.ts'
import { db } from '../db/connection.ts'
import { users } from '../db/schema.ts'

export const register = async (req: Request, res: Response) => {
  try {
    const { email, username, password, firstName, lastName } = req.body

    // Hash password with configurable rounds
    const saltRounds = parseInt(process.env.BCRYPT_SALT_ROUNDS || '12')
    const hashedPassword = await bcrypt.hash(password, saltRounds)

    // Create user in database
    const [newUser] = await db
      .insert(users)
      .values({
        email,
        username,
        password: hashedPassword,  // Store hash, not plain text!
        firstName,
        lastName,
      })
      .returning({
        id: [users.id](<http://users.id>),
        email: [users.email](<http://users.email>),
        username: users.username,
        firstName: users.firstName,
        lastName: users.lastName,
        createdAt: users.createdAt,
      })

    // Generate JWT for auto-login
    const token = await generateToken({
      id: [newUser.id](<http://newUser.id>),
      email: [newUser.email](<http://newUser.email>),
      username: newUser.username,
    })

    res.status(201).json({
      message: 'User created successfully',
      user: newUser,
      token,  // User is logged in immediately
    })
  } catch (error) {
    console.error('Registration error:', error)
    res.status(500).json({ error: 'Failed to create user' })
  }
}