Next.js에서 accessToken
과, refreshToken
을 어떻게 안전하게 다루는지에 대해 고민을 했었다.
일반적으로 accessToken
, refreshToken
다룰 때 localstorage 혹은 cookie에 저장하곤 한다. 두 방식에 대해 장점과 단점을 확인해보면 다음과 같다.
🙂 localstorage
localstorage에 저장하게 되면 장점
CSRF
공격에 안전하다.
localstorage에 저장하게 되면 단점
XSS
에 취약하다. localstorage에 접근 가능한 js코드를 주입하게 되면 원하는대로 삭제하거나 주입시킬 수 있다.
🙂 cookie
cookie 저장하게 되면 장점
XSS
공격에 안전하다.
cookie 저장하게 되면 단점
CSRF
에 취약하다.
그래서 위와 같은 문제를 한꺼번에 해결하기 위해 1-2.Implementation issues
방식을 적용 하였다.
cookie HttpOnly 로 refreshToken을 설정하게 되면 CSRF문제를 해결 할 수 있다. 사용자 혹은 개발자가 code로 브라우저 쿠키에 직접 접근이 불가능 하기 때문이다.
cookie refreshToken 을 주입 시킬 때 expires와 max-age값에 대해 어떻게 적용하면 좋을 지 생각해 봤다. 일반적으로 Session cookie와 Persistent cookie가 있는데 Persistent cookie 값은 브라우저를 껐다 켜도 유지가 된다.
Persistent cookie 기간 설정은 Expries와 Max-Age가 있음
Expires : 만료되는 시간 설정
Max-Age : 얼마동안 유지할 것 인지 설정
처음에 개발을 진행 했을 때 _app.tsx 단에서 getInitialProps로 refreshToken을 통해 새로운 액세스 토큰 받아 왔다. 그 이후에 Client side 단에서 globalAxios default header에 accessToken값을 주입 시키고, 모든 page에서 관련 axios를 사용 가능하게 작업을 진행 하였다.
accessToken : memory 주입
refreshToken : cookie HtttpOnly 저장
그 결과 페이지별 Client side 단에서 useEffect로 api를 호출 시 렌더링 순서 차이로 _app에서 토큰을 주입 시키는 순서보다 더 먼저 실행되어 토큰 없이 api를 호출하는 문제가 발생 하였다.
next page rendering 순서 : _app -> page Component
그래서 _app
단에서 각각의 페이지별로 토큰을 별도로 내려주는 코드가 필요하였고, 페이지별 getServerSideprops와 중복되는걸 피하기 위해 pageProps를 합쳐주는 코드
를 작성 하였다.
class Page extends App<AppProps> {
static async getInitialProps({
ctx,
Component: {getInitialProps: getComponentIntialProps},
}: AppContext): Promise<AppProps> {
...
const pageProps = await (getInitialProps ? getInitialProps(ctx) : Promise.resolve({}))
...
}
return pageProps;
}
이때 위의 두 API는 HTTP 응답 Set-Cookie 헤더
에 refreshToken 값을 설정하고, accessToken을 Json payload
에 담아 보내줘야 한다.
Authentication(인가)는 유저가 요청하는 request를 실행할 수 있는 권한이 있는 유저인가를 확인하는 절차이다.
예를들어 한 화면에서 어떠한 유저는 정보를 확인할 수 있지만, 권한이 없는 유저는 확인을 못 하거나 권한에 맞는 행동만 할 수 있다.
인증 받은 내용(권한 등) 을 웹 쿠키에 저장
이후 미들웨어단에서 쿠키에 접근 해 권한을 얻어오거나
서버 컴포넌트에서 바로 권한 내용을 받아온다.