1. Why?
프로젝트 <모익>에서 회원 관리 및 프론트엔드에서의 보안을 담당하게 되었는데, 그 과정에서 JWT와 OAuth를 사용해서 유저의 인증 상태를 관리했다. 이번 글에서는 어떻게 백엔드와 데이터를 주고받았는지, 그리고 왜 이런 방식으로 구현했는지에 대해서 설명하고자 한다.
만약 네트워크에 대한 배경지식이 없으시다면 아래 링크의 글부터 읽는 것을 추천드립니다.
2. How?
어떤 선택지가 있는지
1) 세션 기반 인증
인증 정보를 서버의 세션 저장소에 저장
- 장점
- 사용자의 인증 정보를 서버에서 안전하게 관리
- 사용 트래픽이 훨씬 적음
- 단점
- 서버에 상태를 유지해야 함
- 서버 자원을 사용
- 세션 불일치 이슈로 인해 수평 확장 시 추가 작업 필요
- 서버에 상태를 유지해야 함
2) 토근 기반 인증
인증 정보를 토큰에 담아 클라이언트에 저장
JWT(Json Web Token)
- 웹에서 사용되는 JSON 형식의 토큰
- JWT는 헤더, 페이로드, 서명으로 구성
- 헤더와 페이로드는 JSON 기반의 key-value 형태
- 서명은 헤더, 페이로드, 암호화키와 헤더에서 정의한 해시 알고리즘을 기반으로 생성
- 모익의 경우에는 SHA-256 사용
- 장점
- HTTP의 Stateless(무상태성)을 유지할 수 있음
- 서버의 확장 시, 수직 확장이 아닌 수평 확장으로도 유지 가능
- 수평 확장(Scale Out)은 수직 확장(Scale Up) 대비 유연성에서 큰 강점이 있기에 주로 사용됨
- 서버 부담 없음
- HTTP의 Stateless(무상태성)을 유지할 수 있음
- 단점
- 토큰 탈취가 가능하기에 보안 문제 발생 가능
- 이미 발급된 토큰의 관리(만료, 취소 등…)가 어려움
OAuth
- 장점
- 복잡한 인증 절차를 외부 서비스에 위임 → 사용자 친화적
- 단점
- 외부 서비스에 대한 신뢰가 필요
- Store에 배포할 경우, 자체적인 회원관리 기능 필요
3. 모익에서 구현한 방식
Moi’c의 로그인 방식
추후 확장성과 서버의 리소스를 고려해 (+ 내가 토큰 관리를 직접 해보고 싶어서) 토큰 기반 인증을 사용했다.
- RESTful API
- JWT와 OAuth를 함께 사용
- JWT
- Store에 올리기 위해서는 자체적인 로그인이 필수
- JWT의 문제점 해결
- OAuth
- UX 향상
- JWT
Moi’c에서 구현한 방법
사용자 인증을 위해 Access Token, 자동로그인과 보안을 위해 Refresh Token 사용
- 사용자는 로그인을 하면 AccessToken은 Response로, RefreshToken은 Cookie로 받는다.
- AccessToken은 항상 header에 추가 되어야 사용자 인증이 가능하다
- AccessToken이 만료되었다면 Refresh요청을 보내며 만료된 AccessToken과 RefreshToken이 담긴 Cookie를 서버로 보낸다.
- 서버에서는 검증 로직을 거친 후 AccessToken을 발급한다.
Moi’c의 AccessToken
- 암호화 키 : 팀원들의 이름을 Base64로 인코딩한 문자열
- 발급자 : moic
- 유효기간 : 30분
- Header
- Key : Authorization
- value : AccessToken
Moi’c의 RefreshToken
- 암호화 키 : 팀원들의 이름을 Base64로 인코딩한 문자열
- 발급자 : moic
- 유효기간 : 30일
- Cookie
- Key : refreshToken
- value : RefreshToken
검증 로직
AccessToken이 유효할 때
- JwtAuthenticationFilter에서 Token 검증
- 토큰의 유효성 검증
- 토큰의 만료 여부 검증
- Token에서 사용자 ID 추출
- 권한 확인 및 서비스 사용
AccessToken이 만료되었을 때
- Front-end에 EXPIRED_TOKEN_ERROR*(HttpStatus.*INTERNAL_SERVER_ERROR*, "SE001", "토큰이 만료되었습니다.") 에러 전송
- Front-end에서 해당 에러를 받으면 /auth/refresh 로 refresh 요청
- 이 때 검증을 위해 Body에 만료된 AccessToken과 RefreshToken이 담긴 Cookie를 전송
- 서버는 먼저 받은 AccessToken을 검증
- RefreshToken도 만료된 토큰인지 확인
- 이후 RefreshToken이 서버에서 발급한 Token이 맞는지 Redis에 확인
- Redis에 저장된 회원 ID와 만료된 AccessToken에서 추출한 회원 ID가 같은지 확인
- 위 검증 과정이 모두 완료되면 AccessToken 재발급
- 만약 Refresh도 만료되어 아래와 같은 response를 받는다면 재로그인 요청
이렇게 해서 좋은 점!!
- AccessToken이 탈취된 경우
- 짧은 유효기간을 갖고 있어 공격자가 재빨리 사용하지 않으면 의미가 없다
- RefreshToken이 탈취된 경우
- refresh 요청은 AccessToken이 있어야 가능하므로 소용이 없다
- 둘 다 탈취되었다면?
- 추후에 좀 더 고민해봐야,,,
4. 래퍼런스
https://hudi.blog/session-based-auth-vs-token-based-auth/
'Project > 모익' 카테고리의 다른 글
런타임을 고려해 타이머 구현하기 (0) | 2024.01.29 |
---|---|
로그인 여부에 따른 접근 제한 처리하기 (0) | 2024.01.29 |
Next.js 13 App Routing PWA 적용하기 / Next.js (0) | 2023.11.30 |
Atomic 패턴과 react-hook-form으로 재사용성을 고려한 로그인&회원가입 컴포넌트 만들기 (0) | 2023.11.30 |