이번 프로젝트에서는 인증 방식으로 JWT 토큰 베이스 인증을 사용하기로 했고, 이를 위해 refresh-token과 access-token 두 가지를 사용하기로 했다. refresh-token은 쿠키에 http-only로 담고, access-token은 요청 헤더에 담기 위해 local storage 등 브라우저 저장소에 담기로 했다.
문제는 서버와 웹 개발 환경의 도메인이 다르다는 점이다. 서버는 AWS EC2에 띄우고 있지만, 웹 프론트엔드는 개발하는 동안 localhost 환경에서 EC2의 서버와 소통해야 하기 때문이다. 만약에 웹을 배포하더라도 프론트엔드와 서버의 도메인이 달라질 것 같다.
cross-domain에서는 CORS 정책으로 인해서 인증이나 쿠키같은 민감한 정보의 교환이 까다롭다. 이번에는 다른 도메인에 쿠키를 설정해 주는 부분에서 애를 먹었다.
CORS를 해결하고 cross-domain에 쿠키를 설정해 주려면 웹 프론트와 서버 양 측에서 설정을 올바르게 해 주어야 한다.
credentials
설정에 include
값을 주어야 한다. Axios의 경우 withCredentials: true
옵션을 주면 된다.credentials
와 preflight
credentials
설정의 기본값은 same-origin
인데, 즉 같은 origin과의 통신에서만 쿠키나 인증 정보를 담겠다는 뜻이다. origin이 같으려면 Protocol(Scheme), Host(Domain), Port가 모두 같아야 한다.
include
설정값은 origin이 다르더라도 모든 요청에 인증 정보를 담겠다는 뜻이다. 대신에 이 값을 쓰려면 한 가지 조건이 추가되는데, 서버에서 응답의 Access-Control-Allow-Origin
헤더에 와일드카드가 아닌 명시적인 URL을 사용해야 하며 그 값이 요청 헤더의 origin 값과 일치해야 한다는 것이다.
보통의 요청에는 본 요청 이전에 preflight라는 예비 요청이 전송된다. 이 요청은 Options 메소드로 전송되며, Access-Control-Request-Headers
Access-Control-Request-Method
헤더를 포함해서 본 요청의 헤더와 HTTP 메소드 정보를 담는다. 그러면 서버는 Access-Control-Allow-*
헤더로 서버의 CORS 정책이 담긴 응답을 보낸다. 브라우저는 요청과 응답을 비교해서 클라이언트의 CORS 정책에 부합하는지 확인하고, 부합한다면 본 요청을 제대로 보내고 아니라면 본 요청을 보내지 않고 에러를 낸다.
스크린샷은 브라우저가 preflight 응답이 마음에 들지 않아서 에러를 낸 모습이다. 본 요청은 보내지지도 않고 CORS error라는 코드 값을 표시한다. 이런 에러가 나면 서버에서 처리를 해주어야 한다.
Access-Control-Allow-Origin
값에 명시적 URL을 등록한다.*
를 쓰면 안된다. 와일드카드는 위의 preflight 에러의 가장 큰 원인이다. whitelist 등을 등록해서 요청의 origin에 따라 적절한 Access-Control-Allow-Origin
을 설정하도록 한다.Access-Control-Allow-Methods
헤더 값에 Options
메소드가 없어서 preflight 요청이 전부 막혔던 적도 있다. 서버에서 Options 메소드를 적절히 처리하고 있는지도 확인해야 한다.Set-cookie
헤더의 쿠키 설정을 올바르게 한다. Samesite=None; Secure
설정이 되어 있는지 확인한다.None
값은 도메인이 달라도 쿠키 전송을 가능하게 한다. Strict
는 같은 도메인에서만 쿠키 전송이 가능하고, 도메인이 다르면 전송이 불가능하다. Lax
는 기본값으로, Strict 설정에서 약간의 예외를 준다. a href, link href
와 같은 일부 경우에만 쿠키를 허용한다.https
를 사용해야 한다. 이 부분은 테스트를 해보질 않아서 모르겠는데, 브라우저의 ssl 인증을 무시하는 몇 가지 방법으로 https를 사용하지 않고도 Secure를 사용할 수 있지 않을까...? 그런데 일단 아래 방법은 임시 방편이고, 가장 올바른 방법은 서버에 도메인과 ssl 인증서를 붙여서 https를 지원하는 것이다. 만약에 아래 방법을 사용한다 하더라도 개발 환경에서만 임시로 켜는 것이 좋을 것 같다.
axios
의 httpsAgent 옵션에 rejectUnauthorized: false
를 추가 import https from "https";
axios.defaults.httpsAgent = new https.Agent({
rejectUnauthorized: false
});
https.globalAgent.options.rejectUnauthorized = false;
package.json
에서 scripts 변경 - "dev": "export NODE_TLS_REJECT_UNAUTHORIZED=0 && npx next dev
next dev
부분에 프로젝트 개발 환경 실행 커맨드를 입력어제 삽질의 가장 큰 원인..
원래 브라우저 개발자 도구의 Application 탭의 쿠키 스토리지에서 쿠키가 설정된 것을 확인할 수 있어야 한다. 그런데 위에서 저걸.. 다 했는데도 개발자 도구에서는 쿠키를 볼 수 없다.
그래서 쿠키가 설정되지 않은 줄 알았는데 사실 쿠키는 설정이 되어 있었다.. 개발자 도구에서 볼 수 없었던 이유는 쿠키의 도메인이 서버 도메인으로 설정되어 있었기 때문이다.
잘 있다... 잘 들어가 있었다... refresh 요청도 잘 된다... refresh-token의 경우에는 클라이언트 쪽에서 열어봐야 할 일이 없기 때문에 이렇게 저장되어 있어도 될 듯 하다.
감사합니다. 이 글을 보고 잃어버린 쿠키를 찾았습니다