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