ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Nest.js] 10 - Authentification - sign up, log in
    Nest.js 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

Designed by Tistory.