Nest.js

[Nest.js] 10 - Authentification - sign up, log in

ESTJames 2021. 9. 13. 19:11

Contents
1. Unique username
2. Error handling
3. Encryption password
4. Log in


1. Unique "username"

- src/auth/user.entity.ts

...

@Entity()
@Unique(['username']) // 1
export class User extends BaseEntity {
 ...
}

// 1. set up "username" column has a unique constaint in its table.


2. Error Handling

- user table

 

- request and response

// when trying to insert a user with the same username, "JinseoCha",
  it responded "500 Internal Server Error", which is not enough information which is occurred in the server.

 

- src/auth/user.repository.ts : add try ~ catch

...

@EntityRepository(User)
export class UserRepository extends Repository<User> {
  async createUser(AuthCredentialsDto: AuthCredentialsDto): Promise<void> {
    const { username, password } = AuthCredentialsDto;
    const user = this.create({ username, password });

    try {
      await this.save(user);
    } catch (error) {
      console.log(error) // 1
      if (error.code === 'ER_DUP_ENTRY') { // 2
        throw new ConflictException('Existing username'); // 3
      } else {
        throw new InternalServerErrorException(); // 4
      }
    }
  }
}

// 1. error in console.log

QueryFailedError: ER_DUP_ENTRY: Duplicate entry 'JinseoCha' for key 'user.IDX_78a916df40e02a9deb1c4b75ed'
    ...
  code: 'ER_DUP_ENTRY', // HERE IS THE CODE !
  errno: 1062,
  sqlMessage: "Duplicate entry 'JinseoCha' for key 'user.IDX_78a916df40e02a9deb1c4b75ed'",
  sqlState: '23000',
  index: 0,
  sql: "INSERT INTO `user`(`id`, `username`, `password`) VALUES (DEFAULT, 'JinseoCha', 'passJinseo')"
}

// 2. error.code === 'ER_DUP_ENTRY' : use the error's property code to throw the exception property.

// 3. ER_DUP_ENTRY : 409 Status code

// 4. nternalServerErrorExeption() : handle other issue to 500 Status code

 

- Handled Response


3. Encryption password

- Bcrypt : plain password -> salt + plain password -> Hashed password
      e.g. :       1234        ->    d1hjskahdj_1234     ->    dsadhwjkalshdjqbhjsabdwahkasjd(something like this) 

- CLI : bcryptjs module

$ npm install bcryptjs --save

 

- src/auth/user.repository.ts

...
import * as bcrypt from 'bcryptjs'; // 1

@EntityRepository(User)
export class UserRepository extends Repository<User> {
  async createUser(AuthCredentialsDto: AuthCredentialsDto): Promise<void> {
    const { username, password } = AuthCredentialsDto;

    // bcrypt password
    const salt = await bcrypt.genSalt(); // 2
    const hashedPassword = await bcrypt.hash(password, salt); // 3

    const user = this.create({ username, password: hashedPassword });

    try {
      await this.save(user);
    } catch (error) {
      console.log(error);
      if (error.code === 'ER_DUP_ENTRY') {
        throw new ConflictException('Existing username');
      } else {
        throw new InternalServerErrorException();
      }
    }
  }
}

// 1. import bcryptjs module into the repository

// 2. await bcrypt.genSalt() : need a unique salt first.

// 3. await bcrypt.hash(password, salt) : make an hashed(encrypted) password

- Request 1

 

- Result 1 in user table

 

- Request 2 with the same password

 

- Result 2 in user table

# Even though the users input passwords are the same, they saved a unique/hashed value in database.


4. Log in

- src/auth/auth.controller.ts

import { Body, Controller, Post } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthCredentialsDto } from './dto/auth-credential.dto';

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}

  @Post('/signup')
  signUp(@Body() authCredentialsDto: AuthCredentialsDto): Promise<void> {
    return this.authService.signUp(authCredentialsDto);
  }

  @Post('/login')
  signIn(@Body() authCredentialsDto: AuthCredentialsDto): Promise<string> {
    return this.authService.signIn(authCredentialsDto);
  }
}

 

- src/auth/user.service.ts

...
import * as bcrypt from 'bcryptjs'; // 1

@Injectable()
export class AuthService {
  constructor(
    @InjectRepository(UserRepository)
    private userRepository: UserRepository,
  ) {}

  async signUp(authCredentialsDto: AuthCredentialsDto): Promise<void> {
    return this.userRepository.createUser(authCredentialsDto);
  }

  async signIn(authCredentialsDto: AuthCredentialsDto): Promise<string> {
    const { username, password } = authCredentialsDto;
    const user = await this.userRepository.findOne({ username });

    if (user && (await bcrypt.compare(password, user.password))) { // 2
      return 'login success';
    } else {
      throw new UnauthorizedException('login failed'); // 3
    }
  }
}

// 1. import bcryptjs module into AuthService

// 2. await bcrypt.compare(password, user.password) : check the input password with encrypted password

// 3. Exception handling : 401 ERROR STATUS

 

- Log in success

 

- Log in failed