← 글 목록

notes

multi domain - auto ( with.cookies)

멀티 도메인 환경의 쿠키 공유·자동 설정과 보안 주의점 메모.

네이버에 한 번 로그인하면 메일, 블로그, 부동산에 들어가도 로그인이 그대로다. 회사 프로젝트도 서비스마다 서브도메인을 나눠 쓸 예정이라, JWT를 쿠키로 공유하는 방식을 따로 정리해 두었다.

  • 로그인 서버가 쿠키를 Domain=example.com으로 굽으면 모든 서브도메인 요청에 같은 쿠키가 따라간다.
  • 각 서비스는 쿠키 안의 JWT를 서버에서 검증만 하면 되고, 쿠키는 HttpOnly와 Secure로 막아 둔다.
  • 도메인 자체가 다르면 쿠키 공유는 안 되고 리다이렉트 방식 인증이 필요하다.

한 번 로그인으로 여러 서비스에

원리는 단순하다. 로그인을 담당하는 서버가 쿠키를 굽을 때 Domain을 부모 도메인으로 잡아 둔다. 그러면 브라우저는 mail.example.com이든 blog.example.com이든 요청할 때마다 그 쿠키를 알아서 실어 보낸다.

네이버도 로그인 쿠키가 .naver.com 범위로 잡혀 있어서 서비스를 옮겨 다녀도 로그인이 유지되는 거였다.

쿠키 범위와 속성

Domain을 아예 안 주면 쿠키는 만든 호스트에만 붙는다. 여기서 처음에 헷갈렸다. auth.example.com에서 로그인했는데 다른 서브도메인에서 쿠키가 안 보였던 게 이 이유였다.

응답 헤더는 이런 모양이 된다.

Set-Cookie: session_token=eyJhbGciOi...; Domain=example.com; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=3600
  • HttpOnly: 스크립트에서 못 읽게 해서 XSS로 토큰이 새는 걸 막는다.
  • Secure: HTTPS에서만 전송된다.
  • SameSite=Lax: 서브도메인끼리는 같은 사이트로 보기 때문에 Lax로도 쿠키가 간다. 다른 사이트에서 마음대로 보내는 POST는 막아 주므로 CSRF도 어느 정도 줄어든다.
  • Max-Age: 토큰 만료와 비슷하게 맞춰 둔다.

부모 도메인에 구운 쿠키는 모든 서브도메인이 받는다는 것도 같이 기억해야 한다. 외부 업체에 맡긴 서브도메인이 있으면 거기에도 토큰이 간다.

Express에서 쿠키 굽기와 검증
// auth 서버: 로그인 성공 후
res.cookie('session_token', jwt, {
  domain: 'example.com',
  path: '/',
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  maxAge: 60 * 60 * 1000,
});

// 각 서비스: 미들웨어에서 검증
function auth(req, res, next) {
  const token = req.cookies.session_token;
  if (!token) return res.status(401).end();
  try {
    req.user = jwt.verify(token, PUBLIC_KEY, { algorithms: ['RS256'] });
    next();
  } catch {
    res.status(401).end();
  }
}

검증용 키는 서비스마다 나눠 줘야 하니까 RS256처럼 공개키로 검증하는 방식이 편하다. 비밀키는 auth 서버만 갖고 있다.

API가 api.example.com처럼 따로 있으면 프론트에서 fetch(url, { credentials: 'include' })로 보내고, 서버는 Access-Control-Allow-Credentials: true와 정확한 Origin을 돌려줘야 쿠키가 실린다. Origin을 *로 두면 안 된다.

로그아웃과 다른 도메인

로그아웃할 때는 쿠키를 만들 때와 같은 Domain, Path로 지워야 한다. 하나라도 다르면 다른 쿠키로 취급돼서 안 지워진다.

res.clearCookie('session_token', { domain: 'example.com', path: '/' });

JWT는 쿠키를 지워도 토큰 자체는 만료 전까지 유효하다. 그래서 access 토큰은 짧게 가져가고, refresh 토큰이나 세션 ID는 서버에 저장해 두고 로그아웃 때 같이 폐기하는 쪽으로 잡았다.

서비스 도메인이 아예 다르면(a.com과 b.com) 쿠키는 절대 공유되지 않는다. 이때는 인증 서버로 리다이렉트해서 코드를 받아 오는 OAuth, OIDC 같은 흐름이 필요하다.