Node.js/NestJs

[User] Model #3 Create Account (+ Hashing Password)

Ykie 2023. 4. 6. 14:13
728x90

users.service.ts / users.resolve.ts

Password Hashing

password를 db에 저장할 때 그대로 저장하지 않는다!! 매우 큰 보안적 위험이 있기 때문에! 
'123' ➡ hashing ➡ 'xxxxxxxxxxxxxxxxxxxxxxxx'  (one way function)
⬅⬅⬅ 이 방향으로 복구 할 수 없다.

// user.entity.ts

@InputType({ isAbstract: true })
@ObjectType()
@Entity()
export class User extends CoreEntity {
...
  @Column()
  @Field((type) => String)
  password: string;
...
  @BeforeInsert()
  async hashPassword(): Promise<void> {
    try {
      this.password = await bcrypt.hash(this.password, 10);
    } catch (e) {
      throw new InternalServerErrorException();
    }
  }
}
// npm i bcrypt, npm i @types/bcrypt -D
// import * as bcrypt from 'bcrypt';

TypeOrm - 모든 Entity는 특정 이벤트를 listen하는 커스텀 로직 메서드를 가질 수 있다.
@beforeInsert()
 - 이 Entity 삽입 전에 데코레이터가 적용되는 메서드를 호출.

GraphQL을 통해 createAccount를 진행하면 password 항목이 변환되어 DB에 저장됨을 확인할 수 있다. 

 

 

service에서 [ok, error] return - error handling.

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User) private readonly users: Repository<User>,
  ) {}

  async createAccount({
    email,
    password,
    role,
  }: CreateAccountInput): Promise<[boolean, string?]> {
    try {
      const exist = await this.users.findOne({ where: { email } });
      if (exist) {
        return [false, 'There is a user with that email already'];
      }
      await this.users.save(this.users.create({ email, password, role }));
      return [true];
    } catch (err) {
      return [false, "Couldn't create account"];
    }
  }
}

resolver가 하는 일은 input을 가지고 output을 내보내는 것 뿐

@Mutation((returns) => CreateAccountOutput)
  async createAccount(
    @Args('input') createAccountInput: CreateAccountInput,
  ): Promise<CreateAccountOutput> {
    try {
      const [ok, error] = await this.userService.createAccount(
        createAccountInput,
      );
      return { ok, error };
    } catch (error) {
      return { ok: false, error };
    }
  }
728x90