토큰 관리 위임과 fetch 기반 RBAC 구현
병원 환경의 헬스케어 서비스라 인증·권한이 특히 민감했다. 클라이언트가 직접 JWT를 다루던 초기 구조엔 보안·중복요청·권한 중복 문제가 겹쳐 있었고, 토큰 관리는 백엔드로 위임하고 RBAC는 미들웨어로 옮겨서 풀었다.
문제
보안성 — 클라이언트가 토큰을 직접 쥠
서버에서 JWT를 응답으로 받아, 클라이언트가 직접 쿠키를 set/delete하고 Authorization 헤더에 넣었다. 이 경우 XSS 공격에 토큰이 노출돼 탈취 위험이 있고, 개발자 도구로도 토큰을 볼 수 있어 보안 수준이 낮았다.
중복 refresh로 로그인이 풀림
Access 토큰이 만료된 순간 여러 요청이 동시에 나가면, 각 요청이 제각각 /refresh를 호출했다.
백엔드의 Redis whitelist 로직과 충돌해 멀쩡히 로그인된 사용자가 로그아웃되는 일이 생겼다.
RBAC 중복
환자·간호사·관리자 권한 분기 로직이 페이지마다 흩어져 있어 복잡도와 유지보수 비용이 높았다.
해결
1·2. 토큰 관리 위임 + 단일 refresh
- 클라이언트의 토큰 set/delete를 걷어내고, HttpOnly 쿠키로 백엔드가 관리하도록 위임했다. JS에서 접근할 수 없으니 XSS 토큰 탈취 표면이 사라진다.
- 첫 번째 401에만
/refreshPromise를 하나 만들고, 이후 동시 요청은 그 Promise를 await 하도록 했다. 중복 refresh가 사라져 로그인이 풀리던 문제도 함께 해결됐다.
fetch 기반 RBAC
Middleware에서 /users/me로 사용자 Role을 파싱해 페이지 접근을 제어했다. 역할별로 갈라져
있던 Loader를 공통화해 권한 로직을 한 곳으로 모았다.
쿠키 속성 — 왜 SameSite Lax · Secure · HttpOnly인가
토큰을 HttpOnly 쿠키로 위임하면서, 쿠키 속성을 하나씩 따져 정했다.
HttpOnly: true— JS에서 쿠키 접근 차단 → XSS 토큰 탈취 방어Secure: true— HTTPS에서만 전송 (단 localhost는 Chrome 89+에서 예외 허용)SameSite: Lax— 사이트 이동 시엔 쿠키를 포함하되, iframe·JS 접근은 제한credentials: include금지 —Access-Control-Allow-Origin: *에 준하는 보안 위험이라, 사내 보안검수에서 막힐 수 있어 사용하지 않음
credentials: include 없이도 로컬(localhost:3000)과 배포 백엔드가 쿠키를 주고받을 수 있었던
건 Next.js 프록시 덕분이다. 프록시가 중개자로 동작해 프론트·백엔드 도메인 차이를 가려, 쿠키가
같은 도메인 요청처럼 처리된다.
결과
- XSS 토큰 탈취 표면 제거
- 동시 요청 시 로그인이 풀리던 문제 해결
- 역할별 접근 제어를 미들웨어 한 곳으로 일원화