안녕!
우아한테크코스 5기 [스탬프크러쉬]팀 깃짱이라고 합니다.
💋 인트로
우테코 프로젝트를 진행하면서 회원가입 및 로그인 기능을 구현해야 했다.
직접 id, password를 입력받아 우리 회원으로 만드는 방식도 고려해 보았지만, 데이터 관리가 보안 측면에서 부담스럽다고 생각해 소셜 로그인을 우리 서비스에 도입하기로 했다.
이번 포스팅에서는 OAuth의 과정에 대해서 알아보려고 한다.
💋 OAuth의 필요성
OAuth의 등장 배경과 필요성에 대해서는 이미 많은 포스팅에서 잘 설명이 되어 있기 때문에 생략하려고 한다.
여기 첨부한 링크에 들어가서 OAuth의 등장배경에 대해서 읽어보면 빠르게 이해할 수 있을 것 같다.
💋 OAuth란?
OAuth(”Open Authorization”)는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로 사용되는, 접근 위임을 위한 개방형 표준이다.
- 사용자가 자신의 계정 정보를 공유하지 않고, 다른 서비스에 대한 접근 권한을 안전하게 제공하는 서비스
- OAuth를 지원하는 서비스는 사용자로부터 인증을 받은 후, 해당 서비스에 대한 접근 권한을 받는다.
OAuth를 한 마디로 요약해 보자면,
‘인증은 유저가 직접, 권한은 서비스에게’
💋 OAuth 2.0 내의 역할들
OAuth는 우리 서비스의 사용자, 그리고 우리 서비스의 서버, 네이버와 같이 우리가 사용할 리소스를 가지고 있는 서버 이렇게 3명으로 이루어져 있다.
✔ Resource Owner
- 내 서비스의 사용자이자, 리소스의 주인
- 인증 절차를 수행 (예. 네이버 로그인)
- 리소스는, 구글의 캘린더 정보, 카카오톡 알림 기능 등이 있다.
✔ Client
- 내 서비스
- OAuth를 이용하여 Resource Server의 인가를 받아 리소스를 이용하고자 하는 서비스
- Client는 Authorization Server로 부터 인가를 받는다.
✔ Resource & Authorization Server
- 내 서비스에서 사용하고자 하는 리소스를 가지고 있는 서버
- 소셜 로그인을 사용하려고 한다면, 예를 들어 네이버, 카카오톡, 구글
- Authorization Server와 Resource Server는 동일한 서버일 수도 있고, 다른 서버일 수도 있다.
- Authorization Server
- 인증, 인가를 하는 서버
- Resource Owner의 인증이 완료되면, Client에게 액세스 토큰을 발급
- Resource Server
- 리소스를 제공하는 서버
액세스 토큰을 활용하여 인가 과정을 수행한 후, 리소스를 제공한다.
- 리소스를 제공하는 서버
- 편의를 위해서 이 포스팅에서는 두 가지를 합쳐서 리소스 서버라고 부르겠다!
💋 OAuth 2.0의 과정
과정을 크게 보면 아래 그림처럼 요약할 수 있다.
각 단계에 대해서 구체적으로 살펴보자!
✔ Client를 Resource Server에 등록한다.
Client가 Resource Server를 이용하기 위해서는, 사전에 승인을 받아 놓아야 하는데, 이 과정을 등록이라고 한다.
등록하는 방법은 서비스마다 다르지만, 공통적으로 Client ID, Client Secret, Authorized Redirect URI를 받는다.
- Client ID
- 내가 만들고 있는 서비스의 식별자
- 노출되어도 상관없음
- Client Secret
- 내 서비스의 비밀번호
- 노출되면 안됨
- Authorized Redirect URI
- 리소스 서버가 권한을 부여하는 과정에서 클라이언트에 Authorized Code를 전달해줄 주소
- 리소스 서버는 사전에 등록된 주소가 아닌 다른 주소로부터 요청이 들어오면 무시한다.
우리 서비스는 네이버 로그인을 구현하기 위해서 리소스 서버인 네이버에 클라이언트인 스탬프크러쉬를 등록했다.
https://developers.naver.com/docs/login/api/api.md
네이버는 등록 과정에서 우리 서비스가 인가를 받을 범위를 함께 지정할 수 있다.
결국 허가는 사용자(Resource Owner)로부터 받아야 하므로, 불필요한 정보는 제외하고 최소한의 기능만큼만 받는 것이 좋다.
Callback URI가 위에서 설명한 Authorized Redirect URI다.
등록이 완료되면, Client ID, Client Secret을 받는다.
이제, 아래와 같은 상황이 연출된다.
Client, Resource Owner는 각각 Client ID, Client Secret을 알게 된다.
Client는 Redirect URL에 해당하는 페이지를 구현하고 준비해 놓고 있어야 한다.
✔ Resource Owner가 Client의 리소스 사용을 승인한다.
리소스에 접근해야만 하는 서비스일 경우에, 클라이언트는 아래와 같은 화면을 리소스 오너에게 보여주게 된다.
Resource Owner가 버튼을 누른다면, 리소스 오너를 리소스 서버의 인증 화면으로 이동시킨다.
리소스 서버는 리소스 오너가 현재 로그인이 되어 있는지 여부에 따라서 로그인이 안되어 있으면, 로그인을 하라는 화면을 보여주고, 리소스 오너가 로그인을 했다면, 아래의 검증 절차를 거친다.
- 위의 URI는 클라이언트 측에서 '네이버 로그인'과 같은 버튼에 넣어놓은 URI다.
- 클라이언트 아이디 값을 리소스 서버에서 가지고 있는지 확인한다.
- 리소스 서버가 저장하고 있는 클라이언트 아이디 1에 대한 Redirect URI와 같은지 확인한다.
하나의 과정이라도 실패하면, 작업을 종료한다.
위의 검증에 성공했다면, 클라이언트에게 scope에 해당하는 권한을 클라이언트에게 부여할 것인지 확인하는 메세지를 전송한다.
리소스 오너가 동의하게 되면, 동의했다는 정보가 리소스 서버로 전송된다.
리소스 서버는 이제 해당 리소스 오너가 해당 scope에 대한 권한을 부여하는 것에 동의했다는 정보를 저장하게 된다.
이 과정까지 마치면, 클라이언트, 리소스 오너, 리소스 서버는 각각 아래와 같은 정보를 가지게 된다.
✔ Resource Server가 Client 정보를 확인하고 승인한다.
리소스 서버가 승인을 하기 위해서 곧바로 액세스 토큰을 발급하지 않는다. 3자간의 일이기 때문에 한 번의 절차가 추가된다.
이 절차가 바로 Authorization Code를 발급하는 일이며, 임시 비밀번호의 개념으로 이해하면 된다.
위에서 scope에 대한 리소스 오너의 승인을 받으면, 리소스 서버는 아래와 같이 사전에 등록한 Redirect URI와 함께 parameter로는 authorization code를 포함한 Location을 응답으로 반환한다.
리소스 오너는 리소스 서버로부터 받은 location으로 redirect해서 클라이언트에 authorization code를 전달한다.
클라이언트는 이제 authorization code를 알게 되었고, 리소스 오너를 통하지 않고 리소스 서버에 곧바로 authorization code를 가지고 접근할 수 있다.
클라이언트가 리소스 서버에 보내는 요청 URI를 더 자세히 살펴보자.
- grant type
- 현재는 authorization code를 사용한 방식으로 인증을 받고 있다.
- 이외에도 4가지 방법이 더 있다.
- redirect uri
- client id
- client secret
중요하게 볼 점은, authorization code, client secret의 두 가지 비밀 정보를 결합해서 리소스 서버에 전송한다는 점이다.
리소스 서버는 아래와 같은 검증 과정을 거치게 된다.
- authorization code를 보고, 리소스 서버에서 가진 정보와 일치하는지 확인한다.
- 어떤 클라이언트에게 발급한 것인지 확인한다.
- 해당 클라이언트의 client secret가 리소스 서버의 정보와 일치하는지 확인한다.
- redirect uri가 사전에 등록된 정보와 일치하는지 확인한다.
위의 정보가 모두 일치하면 다음 단계로 진행한다.
✔ Resource Server가 Client에 액세스 토큰을 발급한다.
OAuth의 목적은 액세스 토큰을 발급받는 것이다.
우선 앞의 결과로 임시 비밀번호인 authorization code까지 확인에 성공했다면, 클라이언트와 리소스 서버는 둘 다 해당 정보를 지운다.
리소스 서버는 액세스 토큰을 만들고, 해당 코드를 클라이언트에게 응답으로 보내준다. 클라이언트는 이 액세스 토큰을 내부적으로 데이터베이스나 파일에 저장한다.
이 액세스 토큰은 해당 클라이언트가 리소스 서버에 접근할 때, user id 1인 사용자에 대해서 b, c에 대한 리소스를 제공할 것을 보장한다.
✔ 액세스 토큰을 포함한 요청으로 Resource Server의 API를 호출해 리소스를 사용한다.
각 리소스 서버는 인증, 액세스 토큰 발급, 갱신, 삭제에 대한 기본적은 API를 이미 만들어서 제공하고 있다.
네이버 OAuth의 API를 아래 예시로 보여주겠다.
위 API들은 모두 액세스 토큰을 요청에 함께 보내야 하기 때문에, 단지 URI로 아무런 액세스 토큰 정보 없이 요청을 보낸다면, 아래와 같은 화면을 만나게 된다.
액세스 토큰을 이제 포함해서 요청을 보내보자!
액세스 토큰을 요청에 포함하는 방법으로는 크게 두 가지가 있는데,
Authorization Header에 넣는 방식과 쿼리 파라미터를 통해 보내는 방식이 있다.
참고로 구글 문서에서는 쿼리 파라미터는 서버 로그에 표시될 수 있으므로, HTTP 헤더에 Authorization으로 토큰을 넣어서 보내는 방식이 더 안전해서 추천한다고 한다.
✔ (선택) 액세스 토큰이 만료된 경우에, Refresh Token을 통해 새로운 액세스 토큰을 발급받는다.
액세스 토큰은 일반적으로 수명이 있다. 짧게는 30분, 1시간이고 길게는 30일, 60일이다.
무튼 이 수명이 끝나고 나면, API에 해당 액세스 토큰을 가지고 가도 더이상 인증을 통과하지 못해 정보를 받지 못한다.
이때마다 사용자에게 위 과정을 다시 거치게 하는 것은 조금 힘들 수 있다. (서비스의 성격에 따라 매번 새롭게 과정을 거쳐야 하는 경우도 있을 것이다.)
앞으로 액세스 토큰을 손쉽게 받기 위해서 리프레쉬 토큰을 활용할 수 있다.
해당 문서를 읽어보면 리프레쉬 토큰에 대해 더 정확히 알 수 있다.
클라이언트가 액세스 토큰을 받을 때, 함께 리프레쉬 토큰을 받는 경우도 있다. (아닐 때도 있음)
둘 다 받는다면, 클라이언트는 두 토큰을 모두 저장해두고 있다가, 리소스에 접근하고 싶을 때는 액세스 토큰을 요청에 보내고, 리소스를 받게 된다.
계속 이렇게 액세스 토큰을 사용하다가, 갑자기 어느날 Invalid Token Error라는 에러가 뜨게 된다면, 이제는 액세스 토큰이 만료되어 더이상 유효하지 않다는 뜻이다.
이렇게 되면 클라이언트는 보관하고 있던 리프레쉬 토큰을 Authorization Server에게 전달하면서, 액세스 토큰을 다시 발급받는다.
영문으로 된 공식 설명은 아래 글을 열자!
(A) The client requests an access token by authenticating with the
authorization server and presenting an authorization grant.
(B) The authorization server authenticates the client and validates
the authorization grant, and if valid, issues an access token
and a refresh token.
(C) The client makes a protected resource request to the resource
server by presenting the access token.
(D) The resource server validates the access token, and if valid,
serves the request.
(E) Steps (C) and (D) repeat until the access token expires. If the
client knows the access token expired, it skips to step (G);
otherwise, it makes another protected resource request.
(F) Since the access token is invalid, the resource server returns
an invalid token error.
(G) The client requests a new access token by authenticating with
the authorization server and presenting the refresh token. The
client authentication requirements are based on the client type
and on the authorization server policies.
(H) The authorization server authenticates the client and validates
the refresh token, and if valid, issues a new access token (and,
optionally, a new refresh token).
리프레쉬 토큰은 갱신되는 리소스 서버도 있고, 아닌 리소스 서버도 있다.
또 리프레쉬 토큰을 갱신하는 방식은 모두 조금씩 다르지만, 대부분 비슷한 플로우이다.
예시로 구글의 refreshing an access token에 대한 내용이다.
아래 경우와 같이 client id, client secret, refresh token을 함께 보내면서, grant type을 refresh_token으로 설정해 /token이라는 end point로 요청을 보낸다.
그러면 구글 서버에서는 해당 리프레쉬 토큰이 유효한지, client id, client secret 값이 맞는지와 함께 해당 클라이언트에게 발급했던 리프레쉬 토큰이 맞는지 확인하고 위 과정이 모두 확인된다면, 응답값으로 새로운 access_token을 발급해 보내준다.
💋 참고자료
- https://developers.google.com/identity/protocols/oauth2/web-server?hl=ko
- https://youtube.com/watch?v=hm2r6LtUbk8&list=PLuHgQVnccGMA4guyznDlykFJh28_R08Q-&index=2
- https://www.youtube.com/watch?v=Mh3LaHmA21I
- https://tecoble.techcourse.co.kr/post/2022-10-24-openID-Oauth/
- https://developers.google.com/identity/protocols/oauth2?hl=ko
- https://developers.naver.com/docs/login/api/api.md
- https://datatracker.ietf.org/doc/html/rfc6749
'WEB > Application' 카테고리의 다른 글
[WEB] 쿠키 VS 세션 VS 토큰 (0) | 2023.09.27 |
---|---|
[WEB] 웹의 발전 과정: WWW부터 CGI, Servlet, JSP, Framework까지 (2) | 2023.09.12 |