OAuth 2.0

inseo24·2023년 8월 26일
0

auth

목록 보기
1/4
post-thumbnail

목적

OAuth 2.0 인가 프레임워크 이해

대상

인증, 인가, OAuth 2.0에 대해 처음 접하는 사람

기본 개념

인증

회사원이라면, 혹은 드라마에서 흔히 회사원들이 출근할 때 이런 게이트에 회사 ID 카드를 찍고 입장하는 것을 보실 수 있습니다.
이게 인증입니다. 회사에선 자사의 사원임을 인증하는 목적으로 회사 ID 카드를 발급해줍니다. ID 카드로 그 회사의 사람인 것을 보증하는 것이죠.

카드 찍고 지나가는 게이트 사진

인가

회사의 모든 사람이 동일한 권한이 있는 게 아닙니다. 사원이 CEO 방을 마음대로 들어갈 수 없는 것처럼 말이죠. 특정 지위를 가진 사람만 접근할 수 있겠죠. 이게 인가입니다. 인증이 된 사용자는 자신이 사용할 수 있는 것이 정해져 있습니다. 여기서는 그걸 Resource라 하며 사용자에게 그 Resource를 사용할 수 있다고 말하는 것을 인가라고 합니다.

방 출입 카드 찍는 사진

왜 할까요?

그 사람인 걸 확인하고 보증하는게 인증, 그 사람이 그 Resource를 쓸 수 있다고 말하는 것이 인가라고 할 때, 우리는 왜 인증, 인가의 과정이 필요할까요?

가장 큰 이유는 우리가 사용자를 기반으로 제품과 서비스를 제공하기 때문입니다. 지금 제가 쓰고 있는 맥북을 예로 생각해보겠습니다. 제 맥북은 아이폰과 같은 계정을 사용하고 있습니다. 같은 iCloud를 사용하겠죠? 제가 휴대폰으로 찍은 사진은 모두 그 iCloud로 자동 업로드가 됩니다. 그 사진은 제 맥북으로도 확인할 수 있습니다.

iCloud 아이콘 사진

그런데 제 사진이 갑자기 엉뚱한 사람의 맥북에서 보인다면 어떻게 될까요? 그럼 저는 아마 iCloud를 사용하지 않을 거고 애플에 대한 신뢰도가 떨어져 사용을 고민할 것입니다. 예시로는 사진을 들었지만 이것은 모든 것이 될 수 있습니다. 애플 페이에 등록한 신용카드가 다른 사람의 애플 페이에 뜬다면요? 이건 심각한 문제가 될 수 있습니다. 사진이든 카드 정보든 앞으로 이 모든 걸 Resource라고 부르겠습니다.

Resource에 누군가 접근하려고 할 때, Resource를 갖고 있는 서버에선 요청한 사람이 정말 권한이 있는 사용자인지 확인을 해야 합니다. 그래야 엉뚱한 사람에게 Resource를 넘겨주지 않기 때문이죠. 그렇기 때문에 어떤 사용자가(인증) 자신에게 권한이 있는(인가) Resource를 요청하는 과정에서 인증, 인가 과정이 필요합니다.

기존 인증 방식과 그 한계

우리는 하루에도 수많은 앱을 사용합니다. OAuth 2.0 이전에는 각 앱이나 서비스가 사용자로부터 직접 정보를 받아 서버에 전달하고 인증이 이뤄졌습니다. 하지만 이렇게 하면 여러가지 문제점이 발생합니다.

  1. 사용자 정보를 해당 서비스에서 모두 저장해야 하고,
  2. 한 서비스에서 너무 많은 정보에 접근할 수 있습니다.
  3. 한 번 권한을 준 서비스를 모아서 한 곳에서 관리하기 힘듭니다.

위 3가지를 정리해보면, 가장 큰 문제는 사용자 정보가 여기저기 흩어진 채로 각 앱이 관리하게 된다는 점입니다. 사용자 정보가 여러 서비스에 걸쳐 분산되어 저장되면, 하나의 서비스에서 보안 문제가 생겨도 연쇄적으로 다른 서비스에서도 문제가 생길 수 있습니다. 또한 앱 별로 사용자는 개인 정보를 모두 제공해야 합니다. 즉, 사용자는 편의성 및 보안성 모두에서 손해를 보게 되는 것이죠.


OAuth 2.0

OAuth 2.0은 위에 언급된 문제를 해결하기 위해 나온 표준입니다.

OAuth 2.0에는 여러가지 용어가 등장합니다. 초반에 용어부터 보면 이해가 어려울 수 있으니 여기서는 예를 들어 먼저 흐름을 정리해보겠습니다.

기존의 인증 방식에는 사용자, 앱, 서버 총 3가지 요소가 있습니다. 사용자가 자신의 개인 정보를 입력해 앱에 전달하고, 앱은 그 정보를 서버에 저장해 보관합니다.
OAuth 2.0은 여기에 한 명의 중재자를 추가합니다. 이 중재자가 바로 인가 서버입니다.

     +--------+                               +---------------+
     |        |--(A)-        인가 요청        ->|               |
     |        |                               |     사용자      |
     |        |<-(B)--       권한 부여       ---|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(C)--       인가 요청       -->|               |
     |   App  |                               |    인가 서버    |
     |        |<-(D)----- Access Token -------|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(E)----- Access Token ------>|    Resource   |
     |        |                               |     Server    |
     |        |<-(F)--- Protected Resource ---|               |
     +--------+                               +---------------+

	            Figure 1: Abstract Protocol Flow                     

용어를 보기 전에 흐름을 한 번 이해하고자 임의로 RFC 문서에 있는 Flow를 수정해봤습니다.

그럼 이제 A부터 F까지 한 번 자세히 살펴보겠습니다.

(A) 인가 요청 & (B) 권한 부여

먼저 앱에서 사용자에게 권한을 요청합니다.
예를 들어, 특정 앱에서 카카오톡으로 회원가입을 진행하면, 아래와 같은 화면을 본 적 있을 겁니다. 카카오톡에 저장된 사용자 정보(이메일 등)를 제 3자인 해당 앱으로 넘길 것인지 사용자에게 인가를 요청하는 절차입니다.

여기서 동의하고 시작하기 버튼을 누르면, 사용자가 자신의 정보에 앱이 접근할 것을 승인하게 되고, 이를 권한 부여라고 합니다.

카카오톡 서비스 가입 화면

(C) 인가 요청 & (D) Access Token 발급

이제 앱은 사용자로부터 받은 권한 부여 정보를 갖고 인가 서버에 요청을 합니다. 여기서 앱은 자신을 인증하기도 하고, 사용자가 준 권한 정보를 전달합니다. 인가 서버는 앱의 요청을 검증하고, 앱이 정상적으로 사용자 권한을 부여 받았다면 그 증거로 Access Token을 발급해줍니다. 이 Token을 이용해 Resource 서버에 요청할 수 있게 됩니다.

(E) Access Token 전달 & (F) Resource 전달

앱은 이제 인가 서버로부터 받은 토큰을 들고 Resource 서버에게 Resource를 요청합니다. 내가 이 Resource에 권한이 있다고 인가 서버한테 확인 받아서 그 증거로 토큰 받았으니, 요청한 것 좀 줘! 라고 말하는 거죠.
Resource 서버는 해당 토큰을 검증하고, 토큰이 유효하면 요청한 Resource를 앱에게 전달합니다.

정리

간단히 말하자면, OAuth 2.0은 사용자, 앱, 인가 서버, Resource 서버 총 4개가 서로 상호작용하며 인증과 인가를 처리하는 메커니즘이라 할 수 있습니다.

각각을 OAuth 2.0의 구성 요소로 보면 아래와 같습니다.

  1. Resource Owner (리소스 소유자)
    사용자를 의미합니다. Resource 소유자는 자신의 데이터에 대한 접근 권한을 부여하거나, 거부할 수 있습니다. 예를 들어, 사용자가 앱에 자신의 카카오톡 정보를 제공할 것인지 결정하는 것입니다.

  2. Client
    사용자가 사용하는 앱을 의미합니다. Client는 Resource 소유자의 권한을 얻어, Resource 서버에 접근하여 필요한 데이터를 가져옵니다.

  3. Authorization Server (인가 서버)
    인증과 인가 과정을 처리하는 서버를 의미합니다. Client가 Resource 소유자로부터 받은 권한을 검증하고, 해당 권한에 대한 토큰을 발급합니다.

  4. Resource Server (리소스 서버): 이는 필요한 데이터를 저장하고 있는 서버를 의미합니다. Client가 인가 서버로부터 받은 토큰을 검증하고, 유효한 경우에만 Client에게 데이터를 제공합니다.

해당 용어를 이용해 각 요소가 어떻게 상호작용하는지 정리하면 아래와 같습니다.

  • Resource Owner와 Client: Client는 Resource 소유자에게 필요한 데이터에 대한 접근 권한을 요청합니다. 이때, Resource 소유자는 자신의 데이터에 대한 접근 권한을 승인하거나 거부할 수 있습니다. 예를 들어, 앱에서 카카오톡으로 회원가입을 할 때, 사용자에게 이메일 등의 정보를 제공할 것인지에 대한 동의를 요청하나 사용자는 이를 승인 또는 거부할 수 있습니다.

  • Client와 Authorization Server: Client는 Resource 소유자로부터 받은 접근 권한을 인가 서버에 전달합니다. 인가 서버는 이를 검증하고, 클라이언트에게 접근 토큰을 발급합니다. 이 토큰은 Client가 Resource 서버에 접근할 때 필요한 키 역할을 합니다.

  • Client와 Resource Server: 마지막으로, Client는 인가 서버로부터 받은 토큰을 사용하여 Resource 서버에 필요한 데이터를 요청합니다. Resource 서버는 토큰을 검증하고, 유효한 경우에만 데이터를 제공합니다.

권한 부여 종류

앞에서 권한 부여란 사용자가 해당 앱에게 자신의 정보에 접근 권한을 주는 것이라 했습니다. 좀 더 자세하게 어떤 식으로 권한이 부여되는지 살펴보려면 그 방식에 여러 종류가 있는 걸 먼저 알아야 합니다.

Authorization Code

  • 사용자가 권한을 부여하면, 인가 서버는 Authorization Code라는 코드를 발급해줍니다. 이 코드를 통해 앱이 Access Token을 얻을 수 있습니다. 보안이 뛰어나 가장 광범위하게 사용됩니다.

Implicit

  • Client 측에서 바로 Access Token을 발급 받습니다. 즉, Authorization Code 단계를 생략하고 바로 토큰을 얻습니다. 당연히 앞서 나온 방식보다 보안이 떨어져 권장되는 방식은 아닙니다.

Resource Owner Password Credentials

  • 말 그대로 사용자의 ID와 비밀번호를 직접 사용해 Access Token을 발급 받습니다. 보안이 가장 취약하므로 신뢰도가 높은 앱에서만 사용하는 것이 좋습니다.(사용자 ID, 비밀번호가 클라이언트 앱에 직접 노출)

Client Credentials

  • 앱 자체가 서버와 통신할 때 사용하는 방식입니다. 즉, 사용자가 개입하지 않는 상황에서 사용되며, 예를 들어, 한 서버가 다른 서버의 리소스에 접근해야 하는 경우에 적합합니다.

방식은 다르지만 결국 누가 누구에게 어떤 것을 줄 수 있는지, 어떤 것을 받을 수 있는지를 다루는 것입니다. 이런 과정을 거쳐 각자 역할에 맞게 권한을 나눠 주고 받게 됩니다. 더 사용자의 정보를 안전하게, 더 효율적으로 관리하기 위함이죠.

이 글에서는 Authorization Code Grant 에 대해서만 추가적으로 설명하겠습니다.

Authorization Code Flow

     +----------+
     | 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)

   Note: The lines illustrating steps (A), (B), and (C) are broken into
   two parts as they pass through the user-agent.

                     Figure 3: Authorization Code Flow

(A) Client Identifier & Redirection URI

앱에서 인가 서버로 Authorization Code를 요청하는 것부터 시작됩니다.
이 때, 앱에선 요청에 여러 정보를 담아서 보내줘야 합니다.

  • client_id
  • scope
  • redirect_uri

인가 서버에 앱 등록

위에서 잠깐 앱이 인가 서버에 요청 시 자신을 인증 한다고 했습니다.
앱이 뜬금없이 갑자기 인가 서버로 요청하는 것이 아니라, 미리 인가 서버로부터 자신의 앱을 등록하고 client id를 부여 받습니다. 앱에 대한 정보를 간략히 등록하고, 자신이 해당 사용자의 정보 중 어떤 정보를 받고 싶은지 scope에 명시합니다. 위의 카카오톡 예시를 다시 보면, 이메일이 이 scope에 해당합니다.

또한 인가 코드(Authorization Code) 요청 성공 후, 해당 토큰을 전달 받을 URI를 인가 서버에 미리 전달해줘야 합니다. 사용자가 인증을 완료하면, 인가 서버는 사용자를 이 redirect_uri로 리다이렉션하게 됩니다. 이는 사용자 인증 정보를 안전하게 전송하는 데 필요하고, redirect_uri를 미리 등록함으로써 인가 서버는 해당 요청이 해당 Client로부터 온 것임을 확인할 수 있습니다.

(B) User Authenticates & (C) Authorization Code

인가 코드(Authorization Code) 요청 시점에 사용자 인증 절차가 이뤄지지 않았다면, 로그인 페이지가 뜨며 인증이 먼저 시작됩니다. 사용자가 직접 계정 정보를 입력하고 인가 서버에서 해당 사용자의 인증 절차를 거칩니다. 사용자 인증이 완료되면 인가 서버에서는 인가 코드(Authorization Code)를 전달하게 됩니다.
( 주의 ) 현재 앱 인증이 완료된 상태는 아닙니다.

Flow에는 없지만, 만약 여기서 해당 앱이 사용자에 대해 권한이 없을 경우 사용자 권한 동의(Consent) 화면으로 넘어가게 됩니다. 이 상태에선 아직 인가 코드(Authorization Code)가 전달되지 않습니다. 앞서 본 카톡 사용자 동의 화면에서 사용자가 동의 버튼을 클릭하면 인가 코드 요청이 성공적으로 이뤄지게 됩니다.

즉, 사용자가 앱에게 권한을 부여한 경우에만 인가 코드(Authorization Code)를 발급해주는 것이죠.

(D) Authorization Code & Redirection URI

인가 코드(Authorization Code)를 발급 받았으면, 모든 인증/인가가 완료된 상태입니다. 앱은 Resource 서버에 Resource를 요청하기 위해 권한 증명을 위해 Access Token이 필요합니다. 이를 위해 인가 서버로 한 번 더 Access Token 발급 요청을 보내게 됩니다.

Access Token 요청 시점에도 아래 데이터를 함께 보내야 합니다.

  • client_id
  • client_secret(or code_verifier)
  • code
  • redirect_uri

여기서 client_id는 알겠고, client_secret이 새로 생겼죠? 이 단계에서 바로 앱 인증이 이뤄지게 됩니다. 해당 요청이 정말로 인가 서버에 등록된 앱인지를 확인하는 절차를 거치는 거죠. 인가 서버에 앱을 등록할 때, client_id만 부여되는 것이 아닙니다. client_secret도 함께 부여되며, 앱은 자신이 인가 서버에 등록된 앱임을 증명하기 위해 id와 secret을 이용합니다.
뒤에 code_verifier는 PKCE(Proof Key for Code Exchange) 방식이라는 Public Client에 대해 사용하는 데이터입니다. 이건 추후 기회가 되면 PKCE(Proof Key for Code Exchange)를 다루는 글을 한 번 써보도록 하겠습니다.

그 뒤에 code는 앞서 발급 받은 authorization code를 의미합니다. redirect_uri도 인가 코드(Authorization Code) 요청 시점에 함께 보낸 그 redirect_uri를 말합니다.

앱에 대한 인증과 인가 코드(authorization code)에 대한 검증이 완료되면 인가 서버에선 Access Token를 전달하게 됩니다. 필요하다면 Refresh Token도 발급됩니다.

다음에는

인증, 인가의 개념과 그 필요성, 그리고 OAuth 2.0에 대한 개념과 권한 부여 흐름을 확인해봤습니다. 한 글에 모든 것을 작성하기에 너무 길어질 것 같으니, 다음 글은 코드와 함께 인증 인가 흐름을 이해하는 글을 쓸 예정입니다.

위 글은 완전히 RFC 문서를 그대로 가져온 것은 아니라 이해를 목적으로 적당히 내용을 뺐고, 문서를 이해하는 데엔 실제 구현된 코드를 보는 것이 더 좋다고 생각해 다음 글은 코드와 함께 돌아오겠습니다. 😇

Reference

profile
나 개발자

0개의 댓글