공식팀의 refreshToken 도입기 ♻️

Sally·2022년 10월 8일
7

왜 refreshToken을 도입했을까?

refreshToken의 경우, 도입하게 되면 관리할 예외상황이 많아져서 리소스가 많이 들어가는 편이다. 그런데도 왜 refreshToken을 도입하게 되었을까?

가장 큰 이유로는 보안유저의 사용성 이 두 가지를 모두 챙기기 위해서 refreshToken을 사용하게 되었다.

refreshToken을 도입하기 이전에는 accessToken에 유효시간을 정해놓지 않았었다. 그리고 해당 accessToken을 공식 사이트의 로컬 스토리지에 저장하고 있었기 때문에 임의로 로컬 스토리지에 진입하여 삭제를 하지 않는 이상 계속해서 로그인을 유지시킬 수 있었다.

유저가 사이트를 들어갈 때마다 로그인 해야 하는 과정이 생략됐다는 점에서는 좋았지만 보안 측면에서는 취약했다. 무한대로 유효한 엑세스토큰이 탈취될 경우 계속해서 해당 엑세스 토큰이 악용될 가능성이 있었다.

그래서, accessToken에 유효시간을 정해놓게 되었다. 유효시간이 지나게 되면 해당 accessToken으로는 공식 서비스의 어떤 권한도 없게 된다.
하지만 유효시간 도입으로 해당 유효시간이 지나게 되면 로그아웃이 되버려 유저가 매번 새롭게 로그인을 해야한다는 번거로움이 생겨버리게 되었다.

어떻게 하면 유저의 사용성과 보안 측면을 모두 챙길 수 있을 까 라는 고민을 결론으로 refresh token을 도입하게 되었다.

refreshToken을 어떻게 활용할까?

refreshToken을 도입하기로 한 이후로 accessToken의 유효시간을 30분으로 짧게 설정해 주었다. 그리고 refreshToken의 경우 유효시간을 2주로 accessToken보다 길게 설정해주었다.

또한 accessToken과 refreshToken을 각각 localstorage, cookie로 다른 장소에 저장해 주었다.

-> 공식은 왜 accessToken을 localstorage에서 저장할까? 🤔

accessToken를 저장하는 곳은 localStorage, sessionStorage, cookie, 프론트의 코드 단 등 다양하게 존재한다. 공식의 경우 프론트단에서 accessToken이 있는 지 여부를 확인해서 로그인 상태인지를 판단하고 로그아웃 시킬때에 프론트에서 accessToken을 삭제하기 위해서 접근이 편리한 web storage를 선택지로 두었다. 그 중에서도 유저가 브라우저를 종료하고 다시 들어와도 로그인 상태를 유지시킬 수 있도록 localStorage를 사용하게 되었다.

-> 공식은 왜 refreshToken을 cookie에 저장할까? 🤔

refreshToken의 경우 프론트 측에서 관리하는 코드가 존재하지 않는다. 그저 백엔드에서의 확인 용도로 사용된다. 그렇기 때문에 cookie를 사용하게 되면 프론트 단에서 여타의 작업이 필요없이 비동기 통신 요청을 보낼 때 refreshToken 담겨져서 백엔드 쪽으로 가게 된다.
추가적으로, refreshToken의 경우 accessToken보다 유효시간이 길어 상대적으로 노출 위험이 크기 때문에 더 잘 숨겨야 할 필요가 있다. 이를 위해 gongseek.site에 저장하지 않고 nginx의 기능을 활용해서 nginx의 도메인에 저장하여 gongseek.site에서는 쿠키 값을 볼 수 없도록 한다.

이렇게 유효시간을 다르게 가져감으로써, accessToken의 유효시간인 30분이 지나도 refreshToken을 통하여 계속해서 유저의 로그인 상태를 유지 시킬 수 있다. 이 때에 코드 단에서 자동으로 처리하기 때문에 유저가 별 다른 요청을 할 필요가 없다.

또한 유효시간도 짧게 가져감으로써 보안성을 높일 수 있다.

공식 팀의 refreshToken Flow 🔄

1. 유저가 로그인을 한다


유저가 로그인에 성공하게 되면 백엔드 측에서는 accessToken과 refreshToken을 발급해주게 된다. 프론트 측에서는 응답값으로 받은 accessToken을 localstorage에 저장한다.
(*refreshToken의 경우 쿠키로 전달되기 때문에 프론트 측에서 따로 조작할 필요 없이 브라우저에 자동으로 저장이 되게 된다)

2. 모든 요청에 accessToken을 담아 보낸다.


프론트 측에서는 모든 비동기 요청의 header에 accessToken을 담아 보내준다. 백엔드에서는 매 통신마다 헤더에 담겨져 온 accessToken이 유효한지 확인하여 유효한 경우 응답값을 보내주게 된다.
그런데 30분이 지나 더 이상 유효하지 않은 accessToken을 전달받게 되면 서버에서는 해당 요청에 대해서 응답값을 보내주지 않고 프론트와 백엔드에서 사전에 약속한 에러 코드 1005번을 담아 에러를 발생시킨다.

3. 프론트에서 다시 재로그인 요청하기


프론트에서는 에러바운더리를 통해서 1005번 에러를 잡아낸다.
1005번 에러의 경우 재로그인 요청을 해달라는 약속을 서로가 미리 한 상태이므로 이를 처리하기 위해서 재로그인 요청을 보내는 로직이 담겨져 있는 페이지로 이동 시킨다.
해당 페이지에서 다시 백엔드에게 재로그인을 해달라는 요청을 보낸다.
이때에도 헤더에는 accessToken을 담아서 보낸다.

4. 백엔드에서 판단하여 재로그인 시켜주기


백엔드에서는 프론트로부터 전달받은 accessToken과 cookie를 통해서 받아온 refreshToken을 가지고 판단을 내린다.
그런데 이 때에는 여러가지 경우의 수가 존재한다.

  1. accessToken과 refreshToken이 모두 정상적일 경우
    accessToken이 이전에 발급된 accessToken이 맞고,
    refreshToken 또한 유효시간(=2주)이 지나지 않고 유저에게 발급된 것일 경우 accessToken과 refreshToken을 재발급한다. 그리고 이를 받은 프론트는 새로운 accessToken을 다시 localstorage에 저장하고 유저의 로그인 상태를 유지시킨다

  2. refreshToken이 유효하지 않고 accessToken이 이전에 발급된 토큰이 맞을 때
    refreshToken이 null이거나 이전에 발급된 refreshToken이 아닌 등의 유효하지 않은 상황이고, accessToken이 이전에 발급된 것이 맞을 경우 1009번 에러를 발생시킨다 그리고 프론트에서는 1009번 에러가 발생하면 accessToken을 지우고 유저를 로그인 페이지로 이동시킨다

  3. refreshToken도 유효하고 accessToken도 유효할 때에
    현재 공식에서는 특정 페이지를 접근하면 재로그인 요청을 하도록 설계되어 있다. 그렇기 때문에 만약 유저가 임의로 해당 페이지의 주소를 url에 입력하여 접근한다면 재로그인 요청이 가게 된다. 그런데 이 때에 accessToken이 유효시간인 30분이 지나지 않아 아직 유효한 경우 재로그인 과정이 필요없기 때문에 백엔드에서는 1010번 에러 코드를 보내주고 프론트에서는 홈페이지로 유저를 이동시킨다

  4. refreshToken은 유효하지만 accessToken이 이전에 발급된 토큰이 아닐 때
    refreshToken이 이전에 발급되었고 유효한 상태이지만 accessToken이 null이거나 이전에 발급된 토큰이 아닌 경우 권한이 없는 유저가 접근하려는 것으로 판단하고 1011에러를 발생시킨다. 프론트에서는 해당 에러코드의 경우 로그아웃을 시키고 refreshToken을 지워달라는 요청을 백엔드에 보낸다. 이후 로그인 페이지로 유저를 이동시킨다.

5. 계속해서 공식을 사용하기


해당 과정을 거치면서 유저의 로그인 상태가 계속해서 유지되고 2주 이내에 유저가 반복적으로 공식 페이지에 들어온다면 영원히(?) 공식 사이트에서 재로그인 할 필요 없이 공식을 편하게 사용할 수 있다.

해당 플로우를 적용한 공식이 궁금하다면?
=> 공식 팀 레포 보러가기

0개의 댓글