Auth를 구현할 때 세션이 아니라 JWT를 이용하는 경우 많은 예제들이 localStorage를 이용한다.
이러한 방식은 다음과 같은 경우 문제가 되지 않을 수 있다.
이런 경우는 토큰의 수명만 적절히 설정해도 거의 문제가 없다시피하다. 하루 한번 로그인, 한달에 한번 로그인, 심지어는 평생 로그인이어도 괜찮을 수 있다.
그러나 이러한 특수한 경우를 제외하고는 조금 더 나은 방식으로 JWT를 저장해야만 한다.
저장소에 따른 보안 문제에 대한 내용은 @yaytomato 님의 다음 글에 자세히 설명되어있다.
중요한것은 XSS는 어떤 방식으로 저장하던 방지해야만 한다는 것이다.
https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/
위 게시물은 하수라에서 추천하는 방식이다. Access Token은 메모리에 저장하고, Refresh Token은 HttpOnly 쿠키에 저장하는 것이다.
만약 이런 단어가 어렵다면 생활코딩의 'WEB' 수업을 들어보길 바란다.
현재 게시물이 올라와있는 이 Velog는 어떤 방식으로 JWT를 다루고 있을까?
깃헙을 분석한 결과 Refresh Token과 Access Token 모두를 HttpOnly 쿠키에 저장하는 방식을 이용하고 있다. @yaytomato 님의 글에 나오는 세번째 케이스와 같다.
결론부터 말하자면 하수라의 방식이 조금 더 JWT의 원래 성격과 더 알맞다. 물론 그것이 무조건 뛰어나다는 것은 아니다. 그 이유를 알아보자.
JWT가 세션에 비해서 가지는 장점으로는, 다양한 마이크로 서비스를 이용하는데에 유용하다는 것이다. 예를 들어 Velog에 로그인 한 경우, 현재 보유중인 Access Token을 통해 Velog와 전혀 다른 저장소와 도메인을 갖지만, Velog와 함께 작동하는 또 다른 서비스에도 사용 할 수 있다. 그리고 그 서비스가 10개든 100개든 1000개든 토큰의 secret만 알고 있다면 어떠한 문제 없이 권한을 인증할 수 있다.
현재 Velog는 velog.io 이외의 도메인에서 이용하지 않고, api서버 또한 같은 도메인에서 제공하기 때문에 굳이 메모리 방식을 사용하고 있지 않다.
그러나 만약 api서버를 프론트엔드 어플리케이션 외부에 분리해야하고, 그 도메인이 다르거나 한다면 하수라의 방식을 이용하는것이 좋다.
내가 제작 중인 프로젝트는 Next.js 프론트 엔드 앱과, Apollo GraphQL API 서버의 도메인이 분리되어있다.(참고로 서브도메인끼리는 쿠키를 공유하지만, 나의 프로젝트는 서브도메인관계조차 아니며, 여러 API 서버를 사용한다.)
호스팅은 Vercel에서 하고 있는데, Next.js 프론트 엔드 소스의 pages/api 폴더 내부에 Serverless Function을 작성하면 프론트 엔드 앱에 API를 쉽게 붙일 수 있다.
물론 GraphQL API를 pages/api 내부에 전부 작성하는 것도 하나의 방법이고, 이렇게 하면 굳이 복잡하게 구현하지 않아도 된다. 쿠키에서 바로 읽어버리면 그만이니까.
그러나 서버를 분리하자면 다음과 같은 순서를 따라야한다. 참고로 이 로직은 Velog의 서버를 많이 참고했다.
구글 OAuth2 를 기준으로 설명하겠다.
이렇게 구글 로그인 이후 총 2번의 API콜을 통해 하수라의 방식을 구현할 수 있음.
로그인 여부 확인에 쿠키를 이용할 수 있다는 것은 Next.js에 굉장히 유용함. 특히나 Next.js의 경우 SSR을 하려고 한다면 Refresh Token이 쿠키에 있는지를 확인하면서 UI를 알맞게 그릴 수 있다는 장점이 있음.
이외에도 매우 다양한 방법이 있음.
Next.js API를 프록시 API처럼 이용하여 HttpOnly 쿠키를 통해 인증
https://maxschmitt.me/posts/next-js-http-only-cookie-auth-tokens/
혹은 next-auth나 next-iron-session 과 같은 라이브러리를 사용하는것도 좋음.
next-auth의 경우 SNS로그인이 매우 쉽고,
next-iron-session은 Ruby on Rails에서 사용하는 방식과 비슷함.
다음번엔 이 두가지 라이브러리들의 방식을 분석해보아야겠음.
참고로 Velog의 Refresh Token Payload는 다음과 같다. token_id 항목은 DB에서 허용하는 token 리스트를 따로 관리하기 때문에 필요하다.
{
"user_id": "XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"token_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"iat": 1606448678,
"exp": 1609040678,
"iss": "velog.io",
"sub": "refresh_token"
}
Access Token Payload는 다음과 같다.
{
"user_id": "XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"iat": 1606461546,
"exp": 1606465146,
"iss": "velog.io",
"sub": "access_token"
}
위 사이트를 통해 각자 자신의 쿠키 정보를 decode하여 볼 수 있다.
안녕 하세요~ 프론트앤드 초보 개발자 입니다. Next js로 구현 테스트중 입니다만,
Next.js 에서 Auth하기(JWT 사용)... 샘플 코드를 받아볼수 있는지요?
받아볼수 있으면 많은 도움이 될거 같습니다...
메일 주소는 pkbond1@hanmail.net 입니다.