Cookie를 사용한 인증 과정에서 겪은 오류들 (feat. Next.js)

krkorklo·2023년 1월 10일
1

현재 Next.js와 express로 프로젝트를 진행하고 있다. 프로젝트의 로그인 로직을 살짝 설명해보자면, 클라이언트에서 로그인 요청을 보내면 서버에서 cookie에 JWT를 저장한다. 인가 관련 요청이 갈 때마다 서버에서 req.cookies를 확인하게 된다.

cookie 부분을 구현하면서 굉장히 우여곡절이 많았다.

첫 번째 위기

그냥 클라이언트 사이드에서 API 요청을 보낼 때는 문제가 없었다.

그런데?!

getServerSideProps로 API 요청을 할 때 url이 invalid하다며 오류가 떴다.

상대 경로가 문제다

문제는 서버 사이드 요청에서 상대 경로로 API 요청을 했기 때문이다. 참고자료

클라이언트 사이드 API 요청이 아니라 서버 사이드에서 요청하는 것이므로 상대 경로 유추가 불가능해서 절대 경로를 명시해야하는 문제였다.

두 번째 위기

절대경로로 API 경로를 명시해주었더니?

이번에는 클라이언트 사이드에서 요청을 보낼 때 CORS 오류가 발생했다. 현재 프로젝트에서는 서버 사이드 요청과 클라이언트 사이드 요청을 함께 사용하기 때문에 해당 부분도 해결해주어야 했다.

서버에서 cors 설정을 해주자

그래도 이 부분은 쉽게 해결되었다.

app.use(cors({ origin: CLIENT_URL, credentials: true }));

서버 측에서 client url의 cors 요청 허용을 해주면 된다.
(클라이언트-서버에서 credentials를 true로 설정해주어야 다른 도메인으로 요청을 보낼 때 인증 정보(쿠키) 전달 가능!)

세 번째 위기

그런데?

그,,, 이게 뭔가 싶겠지만 서버에서 req.cookies를 찍어본 것이다.

지금 분명히 로그인이 된 상황이다. DB에도 반영이 잘 되어있고, cookie 설정 로직이 잘 수행되었는데 req.cookies가 찍히지 않는다. 게다가 크롬 브라우저에서도 cookie가 설정되지 않는다,,,

chrome을 위한 cross site 설정

chrome에서는 도메인이 다르면 cookie가 전송되지 않는다. 이유는 cookie의 SameSite 설정이 default로 Lax가 되어있기 때문이라고 한다.

Samesite
사이트 간 요청과 함께 쿠키에 대한 정책을 정의한다.

  • None : cross site 요청에도 항상 전송
  • Strict : cross site 요청은 항상 전송되지 않음
  • Lax : Strict에 비해 상대적으로 느슨한 정책으로 cross site 요청이 전송되지 않지만 몇 가지 예외적인 상황에 전송

secure
https에서만 cookie가 전송된다.
sameSite를 None으로 하면 secure를 true로 해주어야 한다.

→ 결국 chrome에서 다른 도메인 간에 cookie를 보내려면 sameSite는 None, secure는 true으로 명시를 해주어야 한다.

const cookieOption = {
  httpOnly: true,
  maxAge: 1000 * 60 * 60 * 24 * 30,
  sameSite: 'none' as ISameSite,
  secure: true,
};

res.cookie('accessToken', accessToken, cookieOption);
res.cookie('refreshToken', refreshToken, cookieOption);

네 번째 위기

이제 브라우저에서는 cookie가 저장되는데,, 여전히 서버 측에서는 req.cookies를 읽어올 수 없다.

문제는 SSR

문제는 내가 getServerSideProps로 cookie를 전달하고자 했기 때문이다.

클라이언트 사이드에서는 브라우저에서 쿠키를 저장하고 있다가 서버로 요청이 갈때 자동으로 쿠키를 전송해주기 때문에 문제될 것이 없었다.

하지만 서버 사이드에서는 데이터를 fetch해서 렌더링하고 나서 브라우저로 넘어간다. 결국 cookie를 브라우저가 세팅해주지 않아 직접 쿠키를 전송해야한다.

export const getServerSideProps: GetServerSideProps = async (context) => {
  try {
    const {
      req: { headers },
    } = context;

    if (headers.cookie) {
      instance.defaults.headers.Cookie = headers.cookie;
    }

	const res = await User.getUser();
    ...
  } catch(err) {
    console.log(err)
  }
}

getServersideProps의 파라미터로 들어오는 context는 JS 객체로 http request와 response 등의 정보들을 담고 있다. context 객체를 사용해서 cookie 정보를 가져올 수 있다.

현재 instance 변수는 AxiosInstance이다. getServerSideProps 내에서 default로 cookie를 설정해서 API 요청을 보내게 되면,,,

이제 서버에서 req.cookies를 받아올 수 있다!!!
🥳

0개의 댓글