체크리스트

Refresh Token을 적용하기전 미리 결정한 체크리스트는 다음과 같습니다.

  1. Payload 정보 보호: Refresh TokenPayload에는 탈취당했을 경우를 대비하여 특정할 수 있는 정보를 담지 않기
  2. Redis나 DB 활용: 특정할 정보를 Payload에 담지 않기 때문에, RedisDB를 사용하여 Key(토큰) - Value(유저 정보)의 형태로 관리. 이를 통해 새로운 Access Token을 발급할 때 필요한 정보를 활용하기
  3. 메모리와 DB 저장: 메모리만 사용해서 저장할 경우, 서버가 재시작됐을 때 사용자는 재로그인해야 하는 불편함이 있음. 이를 방지하기 위해 DB에도 함께 저장하기
  4. DB 사용 + 메모리 캐싱: Access Token의 주기가 짧을 경우 잦은 DB 조회가 발생. 이를 해결하기 위해 DB 사용과 더불어 메모리를 이용한 캐싱을 사용하기
  5. Refresh Token의 만료: Refresh Token의 만료 주기는 14일로 설정. 만료된 토큰은 캐시와 DB에서 폐기하기
  6. 사용자 로그아웃 시 Token 폐기: 사용자가 로그아웃하면 아직 만료되지 않은 Refresh Token 폐기하기

적용 내역

Payload 정보 보호

async createRefreshToken(): Promise<Record<string, string>> {
    const refreshTokenUuid = generateUuid();
    const refreshToken = await this.jwtService.signAsync(
      { refreshTokenUuid },
      { secret: jwtConstants.refreshSecret, expiresIn: '14d' },
    );
    return { refreshToken, refreshTokenUuid };
  }

createRefreshTokenData(refreshTokenUuid: string, userUuid: string) {
    const currentDate = new Date();
    const expiryDate = new Date(currentDate);
    expiryDate.setDate(currentDate.getDate() + REFRESH_TOKEN_EXPIRY_DAYS);
    const refreshTokenData: TokenData = {
      token: refreshTokenUuid,
      expiry_date: expiryDate,
      user_id: userUuid,
    };
    return refreshTokenData;
  }

async login(userUuid: string) {
    const { refreshToken, refreshTokenUuid } = await this.createRefreshToken();
    const accessToken = await this.createAccessToken(userUuid);
    const refreshTokenData = this.createRefreshTokenData(
      refreshTokenUuid,
      userUuid,
    );
    super.create(refreshTokenData);
    const tokenData = {
      access_token: accessToken,
      refresh_token: refreshToken,
    };
    return ResponseUtils.createResponse(HttpStatus.OK, tokenData);
  }

Cache**, DB 활용**

const refreshTokenData: TokenData = {
      token: refreshTokenUuid,
      expiry_date: expiryDate,
      user_id: userUuid,
    };