[Node.js] JWT Access / Refresh Token

Manx·2022년 4월 27일
1

Node.js

목록 보기
1/2

JWT를 통해 로그인을 구현해왔다. 자동 로그인을 구현하기 위해 JWT의 만료기간을 최대한 늘리려고 했다.
그러나, JWT의 만료기간을 길게 잡으면 보안에 취약하여 만료기간을 길게 잡으면 안된다.

JWT저장 장소 설정

  1. localStorage
    • CSRF 공격에 안전 ( JS코드에 의해 헤더에 담기므로 사용자인 척 request를 보내기 어렵다. )
    • XSS 공격에 취약하다. ( JS코드 한 줄이면 localStorage를 자유롭게 볼 수 있다. )

  2. cookie에 저장
    • XSS 공격으로부터 localStorage에 비해 안전하다.
    • CSRF 공격에 취약하다.

가장 안전하게 사용하는 방법은 refresh token을 사용하는 방법이고, 나는 어차피 자동 로그인을 위해 사용할 것이니 사용해 보았다.

기본 로직

  • Access Token, Refresh Token 두개 다 발급.
  • Access Token : 만료기간 1시간, Cookie에 저장
  • Refresh Token : 만료기간 2주, db에 저장
  • Access Token 만료, Refresh Token 만료 >> 회원가입 요구
  • Access Token 만료, Refresh Token 유효 >> Access Token 재발급, 쿠키에 다시 넣기
  • Access Token 유효, Refresh Token 만료 >> Refresh Token 재발급, db 수정
  • Access Token 유효, Refresh Token 유효 >> next();

payload에는 _id값만 넣었다. (이것으로 다 조회 가능하니)

db에 저장하니 stateless한 JWT의 장점이 사라진다는 말이 있지만, 만료기간이 긴 Refresh Token을 그대로 노출시킬 수 없었다.
나는 token collection을 따로 생성하여 userId와 함께 저장했다.

JWT Error Handler

  function testVerify(toekn) {
    try {
      return jwt.verify(token, config.jwt.secretKey);
    } catch(err) {
      return err.message;
    }
  }

위 코드로 jwt의 기한이 지났을 겨웅와 키 값이 다른 경우 에러 메세지를 추출해봤다.

  • 기한이 지났을 경우 : Jwt expired
  • 키값이 다른 경우 : Invalid signature

나는 네이티브 앱 API를 만드는 과정이라 Cookie를 사용할 수 없어 Header로 받았다.
Restful한건 {Authorization: Bearer ~} 와 같이 보내야 하지만 편의상 일단 이렇게 진행하였다.

검증 코드

export async function autoLogin(req, res) {
  try {
    var userId = tokenparsing(req.get('Accesstoken'));
    var findrefreshToken = await authRepository.findRefreshToken(userId);
  } catch {
    return res.status(404).json({"status": "404"});
  }
  
  const accessToken = verifytoken(req.get('Accesstoken'));
  const refreshToken = verifytoken(findrefreshToken);
  const newAccessToken = createAccessJwt(userId);
  const newRefreshToken = createRefeshJwt(userId);
  
  if (accessToken === 'invalid') {
    return res.status(401).json({"status": "401"});
  }
  
  // access token, refresh token 만료
  if ((accessToken === null) && (refreshToken === null)) {
    return res.status(401).json({"status": "401"});
  }
  
  await authRepository.updateRefreshToken(userId, newRefreshToken);
  return res.status(200).json({"status": "200", "Accesstoken": newAccessToken});
}

function verifytoken(token) {
    try {
      return jwt.verify(token, config.jwt.secretKey);  
    } catch (err) {
      if (err.message === 'invalid signature') {
        return 'invalid';
      }
      return null;
    }
  }

추가적으로 보안을 위해 Cookie에 tokendb의 index번호만을 넣어서 보낼 수 있다고 한다.


Reference

profile
백엔드 개발자

0개의 댓글