로그인 성공 시 받아온 토큰을 어디에 저장할까?
우선 정해진 답은 없다. 보안의 정도와 서비스 기획 방식에 따라 달라질 것이다.
토큰의 저장위치는 쿠키 vs 로컬스토리지/세션 스토리지 두 경우로 나누어 볼 수 있다.
: 백엔드 작업 시 처리해야할 방법이다.
백엔드에서 로그인이 성공되면 access token을 http header에 넣어 로그인에 대한 응답으로 보내고, 브라우저에서는 이 http 응답 헤더에 존재하는 쿠키를 브라우저에 저장하여 사용한다. 그리고 이후의 모든 http 요청에 대해서는 쿠키를 담아 보내게 되고 stateless한 http에서는 동일 사용자의 접속임을 쿠키를 통해 확인할 수 있다.
쿠키를 사용할 때에도 보안과 관련된 이슈가 있다. CSRF
로 해킹이 가능한데,
이는 이용자의 쿠키값이 요청에 반영되는 것을 이용해서 특정 사이트로 가장한 유사 사이트에 접속을 유도하고, 쿠키값이 반영되어 있는 상태로 개인정보를 유출하게 끔하는 특정 요청을 보내게 되는 것이다. Same-site
정책으로 커버가 가능한데 아직 완전한 상태는 아니다. 그래도 로컬/세션스토리지에 저장하는 것보다는 보안성이 좀더 높다고 할 수 있다.
*
쿠키를 사용하기 위해 처리해주어야 하는 내용 :
프론트 : withCredentials : true
서버 : Access-Control-Allow-Credentials : true
로그인 성공 후 받은 토큰을 스토리지에 저장한다.
로컬스토리지는 브라우저 창이 종료되어도 스토리지 값이 유지되고
세션스토리지에서는 브라우저가 종료되면 해당 스토리지도 삭제되는 차이가 있어 서비스의 내용에 따라 (로그인을 유지할 것인지) 정하기 나름이다.
또한, 간혹 JWT가 쿠키용량 4KB보다 큰 경우엔 스토리지에 저장해야한다.
스토리지에 저장되는 토큰은 최근에는 주로 JWT로 이뤄지는데
스토리지에 저장하는 건 XSS
로 인한 위험성이 있지만
토큰의 유효기간을 설정하는 등 refresh Token과 같이 여러 방면으로 보안의 약점을 신경쓸 수 있다.
쿠키는 자동적으로 토큰이 요청에 붙지만, JS에 저장된 토큰은
아래와 같이 요청 헤더에 작성 해주어야 한다.
Authorization Bearer ${access_token}
(*
XSS로 인한 위험_ 해커가 js에 접근해 script 내용을 수정할 수 있는 것,
token값을 빼내거나 변경시킬 수 있다.)
*
JWT 는 JSON Web Token의 약자로 전자 서명 된 URL-safe (URL로 이용할 수있는 문자 만 구성된)의 JSON
*
인증받을 시, resource Server로 부터 access token
과 refresh token
을 받는다. access token이 유효기간이 있기 때문에, 기간이 만료되면 refresh token을 통해 새로운 access token을 발급받게 되는 것.
1) 로그인이 성공: JWT access token을 발급받는다.
2) 유저정보 요청: 받은 jwt token으로 다시 사용자 정보를 요청한다.
3) 받은 토큰은 로컬스토리지에 저장한다.
4) 유저 정보는 전역상태로 관리한다.
유저의 정보가 필요한 경우가 로그인 헤더에 사용자 정보를 표시해주는 것 외에 많지는 않았지만 이 때 JWT요청으로 받아온 사용자 정보를 전역상태로 저장해주어 필요할때 사용할 수 있었다. (당시에는 recoil
로 관리하였다.)
5) 토큰 유효기간 만료 시 처리
해당 token의 기간이 유효할 경우에만 해당 사이트에 접속이 가능한 상태다.
로그아웃처리가 되며 다시 재접속 하도록 화면을 리다이렉팅 시킨다.
👾아쉬운 점: 유효기간 만료에 대한 처리
지금 내가 글을 쓰고 있는 동안 토큰이 만료되었다고 작성중이던 글이 사라지고 로그인화면으로 바뀌어 버린다면 ??? 안된다. 화 난다. 🤬
작업 당시에는 뭔가 보안을 꼼꼼하게 했네? 라는 생각으로 잘했다고 생각했는데
사용자 경험 측면에서는 최악인 것 같다.
리프레시 토큰의 존재를 알지 못해서 이렇게 되었고, 다음 프로젝트에서는 리프레쉬 토큰을 공부하고 적용해야겠다.
🦄좋았던 점: 사용자 정보를 전역상태로 관리한 점.
사용자 정보를 로컬스토리지에 저장할 수도 있고, 필요할 때마다 사용자 정보를 JWT토큰으로 재 요청해 받아오는 경우가 있다.
스토리지에 저장하는 경우는 JS에 대한 접근 혹은 스토리지 내용의 직접 수정을 통해 유저정보가 변경될 가능성이 있다. 또한, API호출을 통해 사용자 정보를 얻어오는 경우라면 사용자 정보를 사용하는 컴포넌트가 많아 요청이 빈번해질 경우 효율적이지 못하기 때문에 전역 상태로 관리하는 것이 좋다고 생각된다.
위와 같은 상황에서 로그인을 구현하는 상황이라면 다음과 같이 할 예정
1) 로그인 성공하여 JWT토큰을 받는다
2) 쿠키에 access, refresh 모두 저장한다. (보안을 염두했을 때, 젤 Best)
3) JWT토큰을 통해 사용자 정보를 받아오고, 전역상태에 저장시킨다.
(만약 사용자 정보를 활용하는 곳이 많지 않다면 API호출로 받아올 예정)
4) 토큰이 만료되었을 시, refresh token으로 요청을 보내 새로운 access token을 받아오고,
그 token으로 다시 재 요청을 보내 로그인 상태를 유지시키도록 한다.
사용자 정보를 recoil전역으로 저장하면... 새로고침하면 정보가 다 날라가지 않나요?