13. 코드로 만드는 인증_controller

수원 개발자·2023년 12월 11일
0

NestJS

목록 보기
15/15
post-thumbnail

회원가입, 로그인 엔드포인트 만들기

export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Post('login/email')
  loginEmail(
    @Body('email') email: string,
    @Body('password') password: string,
  ) {
    return this.authService.loginWithEmail({ email, password });
  }

  @Post('register/email')
  registerEmail(
    @Body('nickname') nickname: string,
    @Body('email') email: string,
    @Body('password') password: string,
  ) {
    return this.authService.registerWithEmail({ nickname, email, password });
  }
}

서비스에서 만든 함수들을 기반으로 컨트롤러에 엔드포인트를 만들어줬다. 이를 통해 포스트맨으로 잘 작동하는지 시험해보겠다.

이렇게 값들을 중복하지 않게 작성하고 보냈다.

그러면 이렇게 토큰이 발급되었다. 토큰을 해석하기 위해 jwt.io로 가서 해석해봤다.

이렇게 넣은 값대로 잘 바뀐 것을 알 수 있다!

이젠 로그인을 작동시켜보겠다.

로그인 또한 토큰이 잘 형성되었다.

토큰

토큰을 사용하게 되는 방식

1) 사용자가 로그인 또는 회원가입을 진행하면
accesssToken과 refreshToken을 발급받는다.

2) 로그인 할때는 Basic 토큰과 함께 요청을 보낸다.
Basic 토큰은 '이메일:패스워드'를 Base64로 인코딩한 형태이다.

3) 아무나 접근할 수 없는 정보 (private route)를 접근 할 때는
accessToken을 Header에 추가해서 요청과 함께 보낸다.
예) {authorization : 'Bearer {token}'}

4) 토큰과 요청을 함께 받은 서버는 토큰 검증을 통해 현재 요청을 보낸
사용자가 누구인지 알 수 있다.
예를 들어서 현재 로그인한 사용자가 작성한 포스트만 가져오려면
토큰의 sub 값에 입력되어있는 사용자의 포스트만 따로 필터링 할 수 있다.
특정 사용자의 토큰이 없다면 다른 사용자의 데이터에 접근을 하지 못한다.

5) 모든 토큰은 만료 기간이 있어 만료 기간이 지나면 새로 토큰을 발급받아야한다.
그렇지 않으면 jwtService.verity()에서 인증을 통과하지 못한다.
그러니 access 토큰을 새로 발급 받을 수 있는 /auth/token/access와
refresh 토큰을 새로 발급 받을 수 있는 /auth/token/refresh가 필요하다.

6) 토큰이 만료되면 각각의 토큰을 새로 발급 받을 수 있는 엔드포인트에 요청을 해서
새로운 토큰을 발급받고 새로운 토큰을 사용해서 private route에 접근한다.

헤더로부터 토큰 추출하는 로직

async extractTokenFromHeader(header: string, isBearer: boolean) {
    // 'Basic {token}' ->
    // [Basic, {token}]
    const splitToken = header.split(' ');

    const prefix = isBearer ? 'Bearer ' : 'Basic';

    if(splitToken.length !== 2 || splitToken[0] !== prefix) {
      throw new UnauthorizedException('잘못된 토큰입니다.');
    }

    const token = splitToken[1];
    
    return token;
  }

Header로 부터 토큰을 받을 때는
{authorization : 'Basic {token}'}
{authorization : 'Bearer {token}'}의 형식으로 받게 된다.
그렇기 때문에 헤더로부터 토큰을 추출해서 사용할 수 있게 해야한다.
그러기 위해 함수를 다음과 같이 작성했다.

base64로 적힌 token을 해석하기 위해 새로운 함수도 만들어줬다.

decodeBasicToken(base64String: string) {
    const decoded = Buffer.from(base64String, 'base64').toString('utf8');

    const split = decoded.split(':');

    if(split.length !== 2) {
      throw new UnauthorizedException('잘못된 유형의 토큰입니다.')
    }

    const email = split[0];
    const password = split[1];

    return {
      email,
      password,

    }
  }

토큰을 받아 이를 해석해주는 함수를 만들었고 이는 email, password를 추출해서 리턴해준다.

 @Post('login/email')
  loginEmail(
    @Headers('authorization') rawToken: string,
  ) {
    // email:password -> base64
    // decode -> split

    const token = this.authService.extractTokenFromHeader(rawToken, false);

    const credentials = this.authService.decodeBasicToken(token);

    return this.authService.loginWithEmail(credentials);
  }

그렇기 때문에 이를 추출해서 loginWithEmail 함수에 넣으면 토큰으로부터 이메일과 패스워드를 가져와 로그인 할 수 있다.

토큰 재발급 로직

@Post('token/access')
  createTokenAccess(@Headers('authorization') rawToken: string) {
    const token = this.authService.extractTokenFromHeader(rawToken, true);

    const newToken = this.authService.rotatetoken(token, false);

    // {accessToken: {token}}

    return {
      accessToken: newToken,
    };
  }

클라이언트에 전달된 헤더에서 JWT 토큰을 추출하여 extractTokenFromHeader 메서드를 통해 헤더에서 토큰을 추출한다. 또한 rotatetoken을 통해 새로운 토큰을 생성한다.

@Post('token/refresh')
  createTokenRefresh(@Headers('authorization') rawToken: string) {
    const token = this.authService.extractTokenFromHeader(rawToken, true);

    const newToken = this.authService.rotatetoken(token, true);

    // {accessToken: {token}}

    return {
      accessToken: newToken,
    }
  }

이 코드도 마찬가지로 클라이언트에 전달된 헤더에서 JWT 토큰을 추출하여 refresh 토큰일 경우에 새로운 토큰을 생성해준다. 이를 통해 토큰 갱신 및 액세스에 대한 구현을 했다.

0개의 댓글

관련 채용 정보