Dedicats project에서 로그인과 유저 인증 부분을 맡으면서 OAuth와 JWT에 대하여 공부하게 되었다.
이론적으로만 공부했을 때에는 굉장히 복잡하다고 생각했는데,.. 구현을 모두 한 지금 다시 생각해 보니, 코드로서는 굉장히 간단했던 듯 하다.
OAuth 2.0
OAuth 2.0은 외부서비스(third-party application)의 인증 및 권한부여를 관리하는 범용 프레임워크이다.
OAuth 기반 서비스의 API를 호출 할 때에는, HTTP헤더에 accessToken을 포함하여 요청을 보내게 되고, 서비스는 access token을 검사하면서 요청이 유효한지 판단하여 적절한 결과를 응답한다.
OAuth에서 주체는Client, Server(Resource Server, Authorization Server, Resource Owner)로 3명이다.
Client
OAuth 에서 지칭하는 Client는 서비스를 운영하는 측의(확장 기능을 이용하고자 하는) 서버이다.
연동하고자 하는 서비스(google, Facebook)에서 권한을 받고자 하는 입장에서 client이기때문에 client
로 지칭된다.
Server ( Resource server, Authorization Server)
Server는 Google 또는 faceBook 과같이 권한을 부여하는 주체이다.Authorization Server
는 client
의 인증 및 access Token
의 발급을 담당한다.Resource Server
는 access token
을 사용, 요청을 수신 할 때, 권한을 검증 후 결과를 응답한다.
Resource owner
쉽게말해, 어플리케이션을 이용하는 유저이다.
OAuth2 AccessToken Issue Flow
- 이 work flow가 진행되기 전에, 우선적으로
client
는server
에서 우선적으로client id
와client pw
,redirect url
을 발급받아야한다.
셋 모두 외부로 노출 되는것에 민감하겠지만,client pw
와redirect url
은 보안적인 요소이기때문에 특히 더 보안에 유의해야한다.
- Resource Owner는 Client가 제공하는 Server의 확장기능을 이용하려고 한다.
( ex : 구글캘린더로 저장하기, 페이스북 게시물 올리기 등..) - Resource Owner 가 Server에 로그인 되어있지 않은경우, 로그인을 유도하고, 로그인 시 권한동의를 받는다.
- 동의시 Server로 요청을 보내는데, 이 요청에는 먼저 발급받았던
client id
와client secret
,redirect URL
,서버에서의 resource Owner의 아이디
와동의한 권한의 영역
정도의 정보가 전달된다. - Server는 이 모두의 정보가 일치하는지 확인 한 후 Resource Owner에게
Authorization code
를 전달 한다.
Authorization code는 일종의 임시 비밀번호와 같이 생각해도 될것같다.
- Resource Owner은 Client 에
authorization code
를 전달한다. - Client는 Server에
authorization code
와client id
,client secret
,redirect URL
를 모두 전송한다. - Server는 전송받은 정보가 모두 유효할 경우
AccessToken
을 발급해준다. - 이후 AcessToken이 만료되었을때 RefreshToken을 이용하여 accessToken을 재발급해준다. optional 한 방식으로, refresh Token도 재발급 하는 방식도 사용한다고 한다.(무한 로그인의 비밀...!)
- 이후 Resource Server에
accessToken
을 HTTP Header에 넣어 요청하는 방식으로 API요청을 보내고,accessToken
이 유효한 경우 Resouce Server는 적절한 응답을 보내준다.
모바일환경에서 JWT를 이용한 로그인 유지 구현
유효기간이 짧은 Token은 로그인을 자주 해야 해 사용자 경험이 좋지 않을것이고, 유효기간이 긴 토큰은 토큰 탈취 시 보안에 취약하게 된다.
이를 위해 accessToken과 refreshToken을 이용한다.(여기에서 accessToken과 refreshToken은, 모두 JWT이다!)
상대적으로 유효 기간이 짧은 accessToken과 유효기간이 긴 refreshToken을 발급한다.
사용자의 더 디테일한 정보가 담기는 곳은 만료가 빠른 accessToken이며, refreshToken에는 accessToken을 재발급해주는 역할만을 한다.
AccessToken과 RefreshToken을 이용한 로그인 유지 구현은 OAuth2.0에서 인증방식과 동일하다.(동일하지만, 토큰과 JWT라는 차이가 있다!)
클라이언트는 RefreshToken을 보안적으로 안전한 곳에 저장하고, token이 필요한 요청에 accessToken은 HTTP header에 넣어 서버로 요청을 보낸다.
서버는 HTTP request Header에서 token을 꺼내어 token이 유효한지 검사 후 요청을 보낸 user의 id를 해독하여 값을 사용한다.
기본적인 flow는 위와 같다!
그렇지만 클라이언트에서 이를 적용하기 위해서 조금 머리가 아팠던 것 같다.
로그인을 유지하는 방법의 logic은 정말 많이 있겠지만 우리의 프로젝트에서 구현한 방법은 아래와 같았다.
먼저 앱이 구동되면 AuthLoading 페이지가 랜더되고(사실,하얀바탕의 빈화면이다) componentDidmout에서 accessToken을 재발급 받는요청을 보낸다.
accessToken을 재발급 받으려면 refreshToken이 필요하다. (refresh Token을 client에서 저장하는 방법은 보안이 보장되는 하에 자율적이라고 해서, 나는 이 저장소를 react-native의 AsynceStorag에 저장하였다.)
AsyncSorage에 refreshToken이 없을경우 요청을 보내지 않고 Sign in 화면으로 전송된다.
refreshToken이 있을경우, accessToken을 재발급 받는 요청을 보낸다.
- 유효한 refreshToken이라 성공적으로 accessToken을 받았을 경우accessToken을 axios의 request header 기본값으로 설정하고 앱의 mainpage로 navigate한다.
- 유효하지 않은 refresh Token이라, 401요청을 받았을 경우, Signin page로 navigate된다.
Sign in화면서에 유저가 아이디 비밀번호를 입력하고, 로그인에 성공했을 때 서버는 refreshToken만을 발급해주며, 클라이언트는 Authloading 페이지로 navigate되어 main으로의 재접속을 시도한다.
이후 server로 보내는 요청에는 server Router를 통해서 모두 access token이 검증된 후 이루어지며, 만일 accessToken이 유효하지 않다면 서버는 401 응답을 한다.
401응답을 받았을 때 클라이언트는 토큰을 재발급 받는 요청을 보내고, 이에 refreshToken이 유효하지 않다면 로그아웃페이지로, 유효하다면 header의 accessToken을 갱신한 후 작업을 재시도해 달라는 메세지를 띄운다.
아쉬운점
사실 우리 프로젝트의 accessToken은 하루로 잡아놓았었기 때문에 만료되었을 때가 별로 없겠지만, 만료가 되었을때에 에러처리가 조금은 아쉬웠다.
작업을 재 시도해달라는 메세지가 어떻게 보면 사용자 UX에서 그다지 친절하다는 생각이 들진 않았기때문이다.
이 글을 작성하면서, MobX에서 사용하는 token재발급 요청에,작업중이던 요청을 parameter로 첨가하여 (재귀적인 방식)요청을 다시 시도하는 방식이 떠올랐는데, 구현이 가능할지는 내일 아침 미팅때 클라이언트 분들과 조금 더 이야기 해봐야겠다.
참조 : JWT를 사용하는 Bearer
보호된 리소스에 대한 접근 권한을 부여받기 위해 제시하는 유일한 작업이 토큰을 전달 하는 것 뿐 일때 이 토큰을 bearer toekn이라고 부를 수 있다.
따라서, JWT를 사용하는 인증 방식도 사실상 bearer라는 문맥에서 벗어니지 않으며, 단지 bearer token을 생성하기 위해 OAuth 2.0관련 사양을 사용하지 않는 것 뿐이다.
위 설명을 보고 깨닫게 된 것 이지만, OAuth 2.0과 JWT를 사용하는 Bearer는 다른 개념이다! (사실 계속해서 혼동을 하고있었다.)
참조 : JWT vs OAuth
또한 우리의 클라이언트에서도, 아래와 같이 요청을 보내주도록 코딩하였다!
axios.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
참조
내 맘대로 OAuth 2.0정리
생활코딩 OAuth 2.0
JWT Authentication Tutorial - 엄청 친절했던 유투브 강의, 실제로 4-5번정도 들었던것같다.
백앤드가이정도는해줘야함5.사용자인증방식결정
'창고(2021년 이전)' 카테고리의 다른 글
[프로젝트 리뷰] MVC에 대하여 겪었던 고민 (0) | 2020.03.06 |
---|---|
[NodeJS] 에러, 그리고 예외처리(in Express) (0) | 2020.03.05 |
[프로젝트 회고] Dedicats Server 4주 프로젝트 (0) | 2020.03.02 |
React_1 : 리액트, 리액트 프로젝트 시작하기 (0) | 2019.12.12 |
[JS] OOP - Inheritance patterns_상속 (0) | 2019.11.21 |