Oauth2.0 인증 방식은 third-party 어플리케이션에게 리소스 소유자(서비스)를 대신하여 해당 서비스의 접근 권한을 위임하는 방식이다. 회원가입과 로그인 같은 기능을 위임하여 사용자에 대한 인증을 대신 처리함으로 별도의 인증 프로세스 없이 사용자의 신원 확인이 가능하다.
Apple, Google, Facebook, 네이버, 카카오 등 여러 기업에서 OAuth 인증 서비스를 제공하며, API 키를 발급받아 사용할 수 있다.
역할
resource owner
보호된 리소스에 대해 액세스 권한을 가진 자로 일반 사용자(유저)를 뜻한다.
client
보호된 리소스를 접근하여 사용하고자 요청하는 어플리케이션 (개발자가 만든 서비스)
resource server
보호된 리소스를 호스팅 하는 서버로 위에서 언급한 OAuth 인증 서비스 제공자를 뜻한다.
client와 resource owner가 사용하는 리소스를 관리하며 유효성을 체크한다.
이를 접근하기 위해선 access token이 필요하다.
authorization server
resource owner를 인증하고 권한을 얻어 client에게 access token을 발급하는, OAuth 인증의 핵심 로직이 포함된 서버.
OAuth2.0 흐름 개요
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
(A) client 에서 resource owner 로부터 인증을 요구한다. 인증 요청은 resource owner에게 직접적으로 보내지거나, authorization server를 중개자로 절단될 수 있다.
(B) resource owner의 인증을 나타내는 credentials를 client가 받게되며, 4가지 grant type 중 하나를 사용하여 표현한다. authorization grant type은 client에서 사용되는 방법과 authorization server에서 지원하는 방법에 의존된다.
RFC 문서의 4개 방법만 명시. 추가 검색으로는 Refresh Token, Device Code 등이 있다.
Authorization Code Grant
Implicit Grant
Resource Owner Password Credentials Grant
Client Credentials Grant
(C) client 가 authorization server에 인증과 권한부여를 제시하여 access token를 요청한다
(D) authorization server가 client를 인증하고, 권한부여를 입증한다. 유효할 경우, access token을 발행한다.
(E) client 가 access token을 제시하여 인증하고, resource server 로부터 보호된 리소스를 요청한다.
(F) resource server가 access token을 입증하고, 유효할 경우, request를 처리한다.
실제 사용 경험으로 빗대어 위 순서를 간략하게 설명하자면,
사용자(resource owner) -> 서비스(client) : 카카오(OAuth 로그인 서비스 제공)로 로그인하기 요청 (A)
서비스(client) -> 카카오 OAuth 인증서버(authorization server) : 로그인 정보를 전달하여 사용자 인증 후 access token 발급 (B) -> (C) -> (D)
서비스(client) -> 카카오 서버(resource server) : access token으로 유저 정보 등을 요청하여 이를 통해 유저 신원 확인 후 로그인/회원가입 등 프로세스를 진행 (E) -> (F)
OAuth2.0 Authorization Grant 종류
쉬운 이해를 위해 용어는 아래로 통일한다.
resource owner : 사용자
client : 서비스
authorization server : 인증서버
resource server : API서버
authorization code : 인증코드
access token : 접근토큰
refresh token : 갱신토큰
Authorization Code Grant | 권한 코드 방식
서비스와 사용자 사이에서 인증서버가 중재자 역할로 코드를 발급해주며 주고 받는다.
서비스는 사용자를 인증서버로 보내어 인증절차를 처리하고, 이는 인증코드와 같이 사용자가 서비스로 다시 돌아온다.
사용자가 서비스로 돌아오기 전에 인증서버는 사용자를 인증하고 권한을 부여한다. 사용자는 오직 인증서버와 인증하였기 때문에 사용자의 credentials 는 서비스와 공유되지 않는다.
인증코드는 접근토큰을 사용자의 user-agent를 거치지 않고 서비스에 직접적으로 전달하여 사용자를 비롯한 외부에 접근토큰이 노출되지 않는 보안적인 이점을 제공한다.
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
(A) 서비스가 사용자의 user-agent를 인증 endpoint로 보내어 인증절차를 시작한다. 서비스는 service identifier, requested scope, local state, 그리고 redirect URI (인증서버가 user-agent를 인증 후 보낼 주소: 대부분 서비스의 callback 주소)를 포함한다.
https://accounts.kakao.com/login
?continue=https://kauth.kakao.com/oauth/authorize
?is_popup=false
&ka=sdk/1.42.0 os/javascript sdk_type/javascript lang/ko device/MacIntel origin/https%3A%2F%2Fwww.musinsa.com
&auth_tran_id=cmsm7oyfw7ha432f563a20ec43eb463878d3c627079l4ti1zcg
&response_type=code
&state=9f937b06562f5d1628922897c9c7ec3b7ec7296c
&redirect_uri=https://www.musinsa.com/member/sns/login/kakao/callback
&client_id=a432f563a20ec43eb463878d3c627079
&talk_login=hidden
ex) 무신사 스토어의 카카오 로그인하기 URL
중요한 부분은 redirect_uri 이다. 위 링크를 접속하면 카카오 로그인 페이지로 이동하며 ID/PW를 입력하면 각종 개인정보 제공 동의 페이지가 나온다. 그 후, 카카오 인증서버에서 받은 내용을 redirect_uri 인 무신사의 카카오 로그인 callback 링크로 전달한다.
(B) 인증서버가 user-agent를 통해 사용자를 인증하고 사용자가 서비스의 접근 요청을 승인/거부 할지 설정한다.
(C) 사용자가 접근을 허용하면, 인증서버는 user-agent를 redirection URI를 통해 서비스로 보낸다. redirection URI 에는 인증코드와 서비스에서 제공하는 local state 등이 담겨있다.
(D) 서비스는 C에서 받은 인증코드를 인증서버로 보내어 접근토큰을 요청한다. 이 요청에서 서비스는 인증서버와 인증 절차를 가진다. 서비스에는 인증코드 발급을 위해 사용한 redirection URI를 포함되어 있다.
(E) 인증서버가 서비스를 인증하고, 인증코드를 입증한다. 여기서 C에서 사용된 URI와 redirection URI가 동일한지 확인하며, 유효한 경우 접근토큰과 선택적으로 갱신토큰을 같이 반환한다.
Implicit Grant | 암묵적 승인 방식
Implicit Grant는 JavaScript와 같은 스크립트 언어를 사용해 Authorization Code Grant 방식을 브라우저에 최적화하여 간소화시킨 방식이다.
서비스에 인증코드를 발급하는 대신, 서비스는 곧바로 접근 토큰을 발급받는다. grant type은 implicit이기에 인증코드와 같은 중재 증명서를 발급하지 않아도 된다.
접근토큰 발급 과정에서, 인증서버는 서비스를 인증하지 않는다. 경우에 따라 서비스는 접근토큰을 클라이언트에 전달할 때 사용되는 redirection URI를 통해 자신을 입증한다. 이 과정에서 접근 토큰이 user-agent를 통해 사용자 혹은 외부에 노출될 수 있다.
이는 접근토큰을 얻기 위한 절차 수가 줄어들기 때문에 in-browser 어플리케이션과 같은 일부 서비스에서 응답성과 효율성을 향상시킬 수 있다. 하지만, 이러한 편리함은 Authorization Code Grant 방식이 가능한 경우, 보안 사항을 충분히 고려하여 사용해야 한다.
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI --->| |
| User- | | Authorization |
| Agent -|----(B)-- User authenticates -->| Server |
| | | |
| |<---(C)--- Redirection URI ----<| |
| | with Access Token +---------------+
| | in Fragment
| | +---------------+
| |----(D)--- Redirection URI ---->| Web-Hosted |
| | without Fragment | Client |
| | | Resource |
| (F) |<---(E)------- Script ---------<| |
| | +---------------+
+-|--------+
| |
(A) (G) Access Token
| |
^ v
+---------+
| |
| Client |
| |
+---------+
(A) - (B) Authorization Code Grant 방식과 동일하다.
(C) 사용자가 접근을 허용하면, 인증서버는 user-agent를 redirection URI를 통해 서비스로 보낸다. redirection URI 에는 URI 조각 속 접근 토큰을 포함한다.
(D) user-agent 가 redirection 지침을 따라 웹 호스팅 클라이언트 리소스에 요청을 보낸다. [RFC2616] user-agent는 이러한 정보 조각을 로컬에 유지한다.
(E) 웹 호스팅 클라이언트가 스크립트가 내장된 HTML 웹페이지를 반환하여 user-agent가 보유하고 있는 조각을 포함해 redirection URI의 접근이 가능해지며, 이를 통해 조각에 포함된 접근토큰을 추출이 가능해진다.
(F) user-agent 가 웹 호스팅 클라이언트가 제공한 스크립트를 실행하고, 접근토큰을 추출한다.
(G) user-agent가 접근토큰을 서비스에 전달한다.
Authorization Code Grant와 Implicit Grant의 차이
Authorization Code Grant는 인증코드를 통해 접근토큰을 가져오는데, 이과정을 인증서버에서 확인하고 발급한다.
Implicit Grant 방식은 인증서버와 웹 호스팅 클라이언트가 각각 조각을 제공하여 user-agent에서 재조합하여 접근토큰을 조합한다.
서버 리소스는 Implicit Grant가 상대적으로 적게 들지만, 접근토큰이 직접적으로 노출될 수 있어, 안정성이 떨어진다.
Implicit Grant는 갱신토큰을 사용할 수 없다.
Resource Owner Password Credentials Grant | 사용자(리소스 소유자) 자격증명 승인 방식
사용자의 username, password와 같은 자격증명요소를 접근토큰을 얻기 위해 직접적으로 사용할 수 있는 방식이다. 이런 증명요소는 사용자와 서비스 사이에 높은 신용이 있으며, 다른 Authorization Grant가 불가능한 경우에만 사용 가능하다 (예를 들어, 장치OS의 서비스 이거나, high-privileged 어플리케이션:터미널 등).
이 방법에는 사용자의 자격 증명에 대해 직접적인 서비스 접근이 필요하지만, 사용자 자격증명요소는 단일 요청에만 사용되어 접근토큰으로 교환된다. 이를 사용하면, 자격증명요소를 저장할 필요 없이 수명이 긴 접근토큰과 갱신토큰을 통해 지속적으로 사용할 수 있다.
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
(A) 사용자는 서비스에 username과 password를 제공한다.
(B) 서비스가 사용자로부터 받은 인증정보(username/password)를 포함하여 인증서버에 접근토큰을 요청한다.
(C) 인증서버가 서비스와 사용자의 정보를 확인하고, 유효한 경우 접근토큰을 발급한다.
Client Credentials Grant | 클라이언트 자격증명 방식
이 방법은 권한 부여 범위가 서비스가 관리하는 리소스 혹은 인증 서버에 해당 서비스를 위한 접근 권한이 설정되어 있는 경우에 제한하여 사용할 수 있다. 이는 자격증명요소를 안전하게 보관 가능한 서비스에서만 사용되어야 하며, 갱신토큰을 사용할 수 없다.
+---------+ +---------------+
| | | |
| |>--(A)- Client Authentication --->| Authorization |
| Client | | Server |
| |<--(B)---- Access Token ---------<| |
| | | |
+---------+ +---------------+
(A) 서비스가 인증서버와 인증을 거쳐 접근토큰을 요청한다.
(B) 인증버서가 서비스를 인증하고 유효한 경우, 접근토큰을 발급한다.
위 인증절차 내용을 짧게 압축하면 아래와 같이 정리할 수 있다.
Authorization Code Grant | Authorization Code -> Access Token |
Implicit Grant | 로그인 -> Access Token (redirect URI로 전달) |
Resource Owner Password Credentials Grant | Username/Password -> Access Token |
Client Credentials Grant | 클라이언트 자격증명 -> Access Token |
그럼 위 방식은 각각 어떤 경우에 사용가능한가?
우리가 주로 개발하는 웹서비스를 먼저 보면, Authorization Code, Implicit, 그리고 이 글에서 설명하지 않은 PKCE(Proof Key for Code Exchange) 방식을 사용할 수 있다.
여러 부분을 고려했을 때, Implicit 방식은 Access Token이 client-side 에 노출될 수 있어 기피한다.
또한, Authorization Code / PKCE 방식은 Refresh Token를 사용할 수 있다.
Resource Owner Password Credentials 방식은 오직 redirect-based 흐름이 불가능한 경우에만 사용한다. OS와 같이 폐쇄적인 구조의 어플리케이션에서 인증절차를 적용할 때 사용할 수 있다고 필자는 생각한다.
Client Credentials 방식은 client가 resource owner인 경우, 즉 서비스가 자신의 정보를 봐야할 경우 사용된다.
필자는 인증절차가 매우 단순하다보니 서비스 자신에 대해 인증을 하는 경우에 사용한다고 이해했다. 혹시 몰라 예시 링크를 남긴다.
[외부링크]
여러 사항을 고려하였을 때, Authorization Code이 사용하기 가장 안전한 방식이다.
그럼 이 방식을 적용하여 OAuth 인증절차를 개발할 때 유의할 점은 무엇이 있을까?
1. 연동할 OAuth 인증 서비스 선택
Apple, Google, Facebook, 네이버, 카카오 등 OAuth를 제공하는 여러 서비스가 존재한다. 각 서비스마다 App을 등록하고 API 키 발급을 받는 절차가 다양하다. (대부분 진행해보면 비슷하다)
개인적으로 카카오 인증이 제일 단순하면서도 빠르게 구현할 수 있었다.
2. 서비스 앱 등록
각 OAuth 인증 서비스를 등록하면 API 키와 redirect URI를 미리 정하게 된다.
redirect URI는 로컬 테스트를 위해 localhost:PORT 와 production/develop 별 도메인을 설정하여 제한된 환경에서만 테스트를 할 수 있도록 설정해야 한다. 이에 등록되지 않은 URI는 입력을 거절하기 때문에 유의하자.
3. OAuth 연동 시 서비스 테이블 정리
이 부분이 제일 중요하다고 생각한다. 만약 OAuth 인증 서비스를 여러개 붙인다고 생각해보자.
아쉽게도 필자는 백엔드 엔지니어이기에 프론트 페이지의 이슈는 고려하지 않겠다 ㅎㅎㅎ
이렇게 많은 서비스를 연동한다고 했을 때, 서비스의 유저 정보 1개를 어떻게 연결할지 고민될 것이다.
단순히 유저-OAuth인증정보 관계를 1:N으로 정하면, 추후 새로운 인증서비스를 추가하기에 필드 추가와 로직을 수정할 일이 발생할 것이다.
이 경우, 다형성을 고려하여 테이블을 유저-OAuth인증정보-OAuth제공자 관계로 인증정보와 제공자를 M2M 관계로 디자인하는게 최선책이라고 생각한다. 이렇게 되면, 한 계정에 여러개의 소셜 로그인이 가능해지며, 각 인증서비스에서 제공하는 부가적인 기능도 모두 사용가능하다.
RFC 문서를 통해 OAuth2.0에 대해 간단히 알아보았다.
아직까지 트위터와 같이 OAuth1.0a 를 지원하는 플랫폼도 있기에 이는 후에 공부용으로 정리하려 한다.
아무래도 문서를 중심으로 읽어보았기 때문에 좀 더 쉬운 이해를 위해 PAYCO의 과정 프로세스 그림을 붙여둔다..
Reference
'이론 > 웹 | 네트워크' 카테고리의 다른 글
How HTTP request works (0) | 2022.04.24 |
---|