본문 바로가기
기타/보안

[OAuth2] OAuth2 개론 - 개요와 Authorization Code Flow

by 사바라다 2023. 3. 11.

안녕하세요. 이전 포스팅에서는 jwt의 이론과 Java, Spring Boot에서의 실습을 진행해보았습니다.

이번 시간에는 OAuth2의 기본적인 이론에 대해서 알아보도록 하겠습니다.

그리고 이번 포스팅의 이론을 기반으로하여 다음시간에는 실습을 진행해보도록 하겠습니다.

OAuth2란 ?

OAuth(Open Authorization) 2.0는 HTTP 기반의 인증을 위한 업계 표준 프로토콜입니다. OAuth는 리소스를 소유하고 있는 소유자(Resource Owner)를 대신하여 리소스(Resource)에 대한 접근을 제어할 수 있습니다. 이 프로토콜은 접근하고자 하는 리소스를 소유하고 있는 리소스 소유자(Resource Owner)와 리소스 서버(Resource Server), 이를 사용하고자 하는 클라이언트(Client), 그리고 접근 권한을 관리하는 인증 서버(Authorization Server)의 승인 상호작용으로 유저에게 인증과 인가를 제공하여 결과적으로 리소스를 접근하게 할 수 있습니다.

또한 유저에게 사용자 이름 및 비밀번호를 제공받지 않고도 인증서버(Authorization Server)에서의 정보를 통해서 개인 자원을 공유할 수 있습니다. 개인의 리소스(Resource)에는 대표적으로 사진, 비디오, 닉네임, 연락처 목록 등이 포함될 수 있습니다.

이러한 OAuth 2.0은 RFC-6749의 문서로 표준으로 사용되고 있습니다. 모든 OAuth 2.0 구현은 해당 문서 스펙의 Role과 Flow를 기반으로 합니다.

주요 구성요소(Component)와 그 역할 (Role)

OAuth의 주요 구성요소로는 리소스 소유자(Resource Owner), 리소스 서버(Resource Server), 클라이언트(Client), 인증 서버(Authorization Server)가 있습니다. 각 의미에 대해서 알아보도록 합시다.

리소스 소유자(Resource Owner)
접근에 인증이 필요한 리소스(Resource)에 대해서 접근하고자 하는 Entity 입니다. 만약 유저가 이를 이용하고 싶다고 한다면 end-user가 될 수도 있고 만약 다른 서버가 사용을 원한다면 리소스 소유자는 해당 서버가 될 수도 있습니다. 즉, 이는 리소스를 사용하고자 요청을 보내는 주체입니다.

인증 서버(Authorization Server)
리소스에 대한 접근 권한 등을 관리하고 있는 서버입니다. 리소스 서버에 리소스 소유자가 클라이언트를 이용하여 접근할 수 있도록 Access Token을 만들어주는 서버이기도 하며 Grant Type을 관리하고 인증 정보를 관리하기도 합니다.

리소스 서버(Resource Server)
리소스가 존재하고 있는 서버입니다. 이 서버는 리소스를 가지고 있고 해당 리소스를 접근 권한이 있는 사용자에게만 접근할 수 있도록 허용합니다. 대표적으로 JWT를 Access Token으로 이용한다면 리소스 서버는 이를 verify하여 인증과 인과를 확인하여 접근 여부를 판단합니다. 간단히 말해서 정보를 가지고 있는 API 서버라고 볼 수 있습니다.

클라이언트(Client)
리소스 소유자를 대신하여 리소스 서버에 리소스를 요청하는 어플리케이션입니다. 웹, 모바일 앱, 데스크 탑 어플리케이션이라고 봐주시면 됩니다.

참고로 인증 서버와 리소스 서버는 구현에 따라 동일한 서버가 될 수도 있고 나뉘어질수도 있습니다.

Protocol Flow

위의 이미지는 OAuth 2.0에서 4가지의 Role의 추상화된 상호작용을 flow로 나타낸 것입니다. 순서는 위에서 아래의 순서를 가집니다.

  1. 클라이언트는 리소스 소유자에게 권한얻기 위해서 인증을 요청합니다. 권한 부여는 리소스 소유자가 직접 할수도 있습니다. 하지만 대체적으로 로그인 등과 같이 인증 서버에 위임하여 간접적으로 요청하는것이 일반적입니다.
  2. 인증이 완료되면 Client는 authorization grant중 한가지와 이에 해당하는 정보들을 받습니다. (authorization grant의 종류는 아래에서 추가적으로 알아보겠습니다.) 권한 부여 유형은 기 정의되 내용으로 각 서비스마다 다를 수 있습니다.
  3. 2번에서 얻은 정보와 Client가 기존에 가지고 있던 정보를 가지고 인증 서버(Authorization Server)에 Access Token을 요청합니다.
  4. 인증 서버는 클라이언트가 전달한 정보 및 Authorization Granth의 유효성을 검증합니다. 검증이 정상적으로 완료되면 Access Token을 발급해줍니다.
  5. 클라이언트는 Access Token을 이용하여 리소스 서버에 리소스에 접근한다는 요청을 합니다.
  6. 적절한 Access Token인지 Resource Server에서 검증한 후 검증이 완료되었다면 서버에서 리소스를 반환합니다.

위의 Flow를 보았을 때 실제로 리소스 소유자가 본인을 인증하는 것은 1번밖에 없습니다. 나머지는 시스템끼리 주고 받는 flow입니다. 1번의 인증 이후에 실제로 리소스에 접근하기 위해서 필요한것은 Access Token입니다. 결과적으로 리소스 접근에 대해서 유저의 Access Token이 만약 탈취되더라도 인증 정보는 무사하기 때문에 Access Token만 만료 시키면 안전성을 유지할 수 있습니다.

Authorization Grant의 종류

위의 flow 중 2번에서 리소스 소유자의 인증에 의해서 Authorization Grant중 하나를 가져온다고 하였습니다. Authorization Grant란 Access Token을 얻는 방법입니다. OAuth2는 여러 방법으로 Access Token을 얻는 방법을 제공하고 있습니다. 정의되어있는 Authorization Grant들은 다양한 방법이 있습니다. 먼저 그 리스트를 보도록 하겠습니다.

  • Authorization Code
    • 클라이언트와 리소스 소유자 사이에 인증 서버를 두고 access_token를 얻는 flow를 가집니다. 클라이언트는 인증 서버를 통해서 웹 사이트가 열리고 이곳에서 리소스 오너라는 것을 인증합니다. 그 이후 code를 받고 이를 access_token으로 교환하는데 이러한 인증 flow에는 클라이언트가 개입하는 부분이 없는 Authorization Grant 입니다.
  • Implicit
    • 이 유형은 Authorization Code grant에서 인증 코드 교환과정을 빼고 바로 access_token을 발급받는 방법입니다. 권장하는 방법은 아닙니다.
  • Resource Owner Password Credentials
    • 클라이언트에서 직접 username과 password를 인증서버로 보내서 인증하고 access_token을 발급받는 방법입니다.
  • Client Credentials
    • 클라이언트에 client_id와 client_secret을 저장해두고 이를 이용해서 인증하고 access_token을 발급받는 방법입니다. 이는 서버간 통신에 사용되곤합니다.

각 방법들의 차이는 디테일한 flow에 대한 차이가 있습니다. 오늘은 위 리스트 중 가장 흔하게 많이 사용되는 Authorization Code에 대해서 자세히 알아보도록 하고 나머지는 이후에 알아보도록 하겠습니다.

Authorization Code

클라이언트와 리소스 소유자 사이에 인증 서버를 두고 access_token를 얻는 flow를 가집니다. 클라이언트는 인증 서버를 통해서 웹 사이트가 열리고 이곳에서 리소스 오너라는 것을 인증합니다. 그 이후 code를 받고 이를 access_token으로 교환하는데 이러한 인증 flow에는 클라이언트가 개입하는 부분이 없는 Authorization Grant 입니다. 자세한 flow는 아래와 같습니다.

이러한 Authorization Code grant type은 웹과 모바일 앱에서 주로 사용됩니다. 해당 type이 다른 grant type과 크게 다른 점은 클라이언트에서 인증하는 것이 아니라 인증 서버를 통해서 이동된 웹 사이트를 통해 유저가 인증하고 이 정보가 사용된다는 점에 있습니다.

위 flow를 4단계로 구분하면 아래와 같습니다.

  1. 클라이언트 어플리케이션은 Browser를 열고 사용자는 OAuth Server로 접속합니다.
  2. 사용자는 App의 요청을 승인하도록 새롭게 뜬 prompt 창을 보게 되고 로그인, 승락 또는 거절을 할 수 있습니다.
  3. 승낙하게 되면 클라이언트 어플리케이션으로 redirection 되고 authorization code를 받습니다.
  4. 클라이언트 어플리케이션은 이를 access token과 바꾸고 유저에게 반환합니다.

각 단계에서 주고 받는 정보를 확인해보도록 하겠습니다.

리소스 소유자가 인증 서버에 권한을 요청할 때 호출하는 API

1번에서 클라이언트가 인증 서버에 요청하는 내용입니다. 이는 아래와 같습니다.

https://{{인증 서버 주소}}/auth
 ?response_type=code
 &client_id=29352915982374239857
 &redirect_uri=https%3A%2F%2Fexample-app.com%2Fcallback
 &scope=create+delete
 &state=xcoiv98y2kd22vusuye3kch
  • response_type=code : 인증 서버에 어플리케이션은 authorization code flow로 인증을 할것임을 전달
  • client_id : 클라이언트를 지칭하기 위한 key로 인증 서버에 등록되어있는 client_id
  • redirect_uri : 인증이 승인 되었을 때 user를 되돌리는 url 주소
  • scope : 요청할 권한 리스트
  • state : 클라이언트에서 생성한 random string 값. 이를 포함함으로써 승인 되었을 때 redirect_uri에 state를 그대로 반환 받습니다. 이를 이용하면 client에서는 정상적인 인증 서버에서 값이 반환되었다고 알 수 있습니다. 이를 통해서 CSRF 공격을 막을 수 있습니다.

인증 서버에서 노출되는 로그인 창은 이후 실습에서 보여드리도록 하겠습니다.

클라이언트 URL로 돌아오는 Redirection API

유저가 인증 서버에서 본인 인증을 완료하고 나면, 인증 서버는 redirect_uri로 사이트를 이동시킵니다. 이때 아래의 파라미터를 함께 전달한다.

  • state : client가 request에 함께 보낸 state와 동일한 값입니다. client는 이값을 이용해서 정상적인 곳에서 해당 redirect_uri를 호출했는지 확인해야합니다. 일렇게 함으로써 CSRF 공격을 막을 수 있습니다.
  • code : 인증 서버가 생성한authorization code입니다. 이 code는 1분 ~ 10분 정도의 짧은 TTL을 가지며 이후 Access Token과 교환이 가능합니다.
https://{{클라이언트 주소}}/redirect
 ?code=g0ZGZmNjVmOWIjNTk2NTk4ZTYyZGI3
 &state=xcoiv98y2kd22vusuye3kch

인증 코드(Authorization Code)와 Access Token을 교환하는 API

이제 클라이언트는 위에서 받은 code를 이용하여 access token을 얻는 step만이 남아있습니다.

아래의 파라미터로 access_token에 요청합니다. 이 request는 POST method를 이용합니다.

  • grant_type=authorization_code : grant type을 Authorization Code로 이용하고 있다고 전달합니다.
  • code : authorization server에서 받은 code를 전달합니다.
  • redirect_uri(optional) : access_token을 반환받고 이동할 곳의 url 입니다.
  • client_id : client를 지칭하기 위한 key로 인증 서버에 등록되어있는 client_id
  • client_secret : client의 secret. 유저와는 상호작용 없이 클라이언트와 인증 서버간의 뒷단에서 통신이 이루어집니다. 따라서 이렇게 하면 인증 코드(authorization code)를 가로챘을 수 있는 잠재적 공격자가 아닌 애플리케이션에서만 액세스 토큰을 요청할 수 있습니다.

이런 과정을 거쳐 결과적으로 클라이언트는 access_token을 얻게 되며 리소스 서버는 이를 검증하고 정상적인 token이면 유저에게 해당 리소스에 대한 접근을 허락해줍니다. 인증 과정의 마지막 결과로 얻게 되는 response는 아래와 같습니다.

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
  "token_type":"bearer",
  "expires_in":3600,
  "refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
  "scope":"create delete"
}

Authorization Code Flow는 언제 사용하면 좋을까 ?

The Authorization Code flow는 웹과 모바일 앱에서 사용하기 가장 좋은 방법입니다. 위에서 봤던 대로 해당 Grant Type에는 authorization code와 access token을 변환하는 과정이 있습니다. 해당 과정은 추가적인 보안 계층을 제공해주는 역할을 합니다. 사용에 주의사항으로는 client secret을 저장하면 안됩니다. 코드 교환 단계에서는 액세스 토큰이 항상 애플리케이션과 OAuth 서버 간의 보안 백채널을 통해 전송되므로 공격자가 액세스 토큰을 가로챌 수 없습니다. (유저의 interaction과 관계 없으며 유저는 이 flow를 모른다.)

마무리

오늘은 이렇게 OAuth2의 기본적인 이론에 대해서 전체적으로 한번 살펴보는 시간을 가져보았습니다.

사실 처음 이러한 과정을 접하면 인증 과정이 상당히 까다롭다고 느끼실 수 있습니다.

다음시간에는 위 내용중 Authorization Code Grant 방법으로 직접 실습해보는 시간을 가져보도록 하겠습니다.

오늘 다루지 못한 다른 인증 방법들에 대해서는 또 별도의 시간을 통해서 전달드릴 수 있도록 하겠습니다.

감사합니다.

참조

[1] Spring Security In Action

[2] https://www.rfc-editor.org/rfc/rfc6749

[3] https://oauth.net/2/

[4] https://aaronparecki.com/oauth-2-simplified/

[5] https://developer.okta.com/blog/2018/05/24/what-is-the-oauth2-implicit-grant-type

[6] https://developer.okta.com/blog/2018/06/29/what-is-the-oauth2-password-grant

[7] https://developer.okta.com/blog/2018/04/10/oauth-authorization-code-grant-type

[8] https://www.ibm.com/docs/ko/sva/9.0.7?topic=SSPREK_9.0.7/com.ibm.isam.doc/config/concept/con_oauth_20_concepts.htm

댓글