OAuth 2.0 설명: 제로에서 영웅까지
Daniel Hayes
Full-Stack Engineer · Leapcell

OAuth 2.0에 대한 간결하고 이해하기 쉬운 설명
OAuth는 전 세계적으로 널리 사용되는 인증을 위한 개방형 네트워크 표준이며, 현재 버전은 2.0입니다. 이 기사에서는 RFC 6749를 기반으로 OAuth 2.0의 설계 개념과 작동 프로세스에 대한 간결하고 이해하기 쉬운 설명을 제공합니다.
I. 적용 시나리오
OAuth의 적용 시나리오를 이해하기 위해 먼저 가상 예제를 살펴보겠습니다. 사용자가 Google에 저장한 사진을 인쇄할 수 있는 "클라우드 사진 인쇄"라는 웹사이트가 있다고 가정합니다. 사용자가 이 서비스를 사용하려면 "클라우드 사진 인쇄"가 Google에 저장된 사진을 읽도록 허용해야 합니다.
여기서 중요한 문제는 Google은 사용자 권한을 얻은 후에만 "클라우드 사진 인쇄"가 이러한 사진을 읽도록 허용한다는 것입니다. 그렇다면 "클라우드 사진 인쇄"는 어떻게 사용자 권한을 얻을 수 있을까요?
전통적인 방법은 사용자가 Google 사용자 이름과 비밀번호를 "클라우드 사진 인쇄"에 알려주어 "클라우드 사진 인쇄"가 사용자의 사진을 읽을 수 있도록 하는 것입니다. 그러나 이 접근 방식에는 다음과 같은 심각한 단점이 많습니다.
- 비밀번호 저장의 보안 위험: 후속 서비스를 제공하기 위해 "클라우드 사진 인쇄"는 사용자의 비밀번호를 저장하여 큰 보안 위험을 초래합니다.
- 로그인 방법의 보안 문제: Google은 비밀번호 로그인 방법을 사용해야 하지만 간단한 비밀번호 로그인은 보안이 취약합니다.
- 권한 제어 부족: "클라우드 사진 인쇄"는 사용자가 Google에 저장한 모든 자료에 액세스할 수 있는 권한을 가지며 사용자는 "클라우드 사진 인쇄"가 얻은 권한의 범위와 유효 기간을 제한할 수 없습니다.
- 권한 취소 비용 증가: 사용자는 비밀번호를 변경하여 "클라우드 사진 인쇄"에 부여된 권한만 취소할 수 있습니다. 그러나 이렇게 하면 사용자 권한을 얻은 다른 모든 타사 응용 프로그램이 무효화됩니다.
- 데이터 유출 위험 증가: 하나의 타사 응용 프로그램이 해킹되는 한 사용자 비밀번호와 비밀번호로 보호되는 모든 데이터가 유출될 수 있습니다.
그리고 OAuth는 바로 이러한 문제를 해결하기 위해 탄생했습니다.
II. 명사 정의
OAuth 2.0을 자세히 설명하기 전에 먼저 몇 가지 전문 용어를 이해해야 합니다. 이러한 용어는 특히 관련 다이어그램을 포함하여 후속 내용을 이해하는 데 매우 중요합니다.
- 타사 응용 프로그램 (Third-party Application): 이 기사에서는 "클라이언트"라고도 하며, 이전 섹션의 예에서 "클라우드 사진 인쇄"와 같습니다.
- HTTP 서비스 (HTTP Service Provider): 이 기사에서는 "서비스 제공자"로 약칭되며, 이전 섹션의 예에서 Google과 같습니다.
- 리소스 소유자 (Resource Owner): 이 기사에서는 "사용자"라고도 합니다.
- 사용자 에이전트 (User Agent): 이 기사에서는 브라우저를 의미합니다.
- 인증 서버 (Authorization Server): 즉, 서비스 제공자가 인증을 처리하기 위해 특별히 사용하는 서버입니다.
- 리소스 서버 (Resource Server): 즉, 서비스 제공자가 사용자가 생성한 리소스를 저장하는 서버입니다. 인증 서버와 동일한 서버이거나 다른 서버일 수 있습니다.
위의 명사를 이해한 후에는 OAuth의 역할을 이해하기 어렵지 않습니다. 즉, "클라이언트"가 "사용자"의 권한을 안전하고 제어 가능한 방식으로 얻은 다음 "서비스 제공자"와 상호 작용할 수 있도록 하는 것입니다.
III. OAuth의 개념
OAuth는 "클라이언트"와 "서비스 제공자" 사이에 인증 계층을 설정합니다. "클라이언트"는 "서비스 제공자"에 직접 로그인할 수 없지만 인증 계층에 로그인하여 사용자를 클라이언트와 구별합니다. "클라이언트"가 인증 계층에 로그인하는 데 사용하는 토큰은 사용자의 비밀번호와 다르며 사용자는 로그인할 때 인증 계층 토큰의 권한 범위와 유효 기간을 지정할 수 있습니다.
"클라이언트"가 인증 계층에 로그인하면 "서비스 제공자"는 토큰의 권한 범위와 유효 기간에 따라 사용자 저장 자료를 "클라이언트"에 엽니다.
IV. 작동 과정
OAuth 2.0의 작동 과정은 다음 그림에 나와 있습니다 (RFC 6749에서 추출).
- (A): 사용자가 클라이언트를 열면 클라이언트는 사용자에게 권한을 요청합니다.
- (B): 사용자가 클라이언트에 권한을 부여하는 데 동의합니다.
- (C): 클라이언트는 이전 단계에서 얻은 권한을 사용하여 인증 서버에서 토큰을 신청합니다.
- (D): 인증 서버는 클라이언트를 인증하고 문제가 없으면 토큰을 발급하는 데 동의합니다.
- (E): 클라이언트는 토큰을 사용하여 리소스 서버에 리소스 획득을 신청합니다.
- (F): 리소스 서버는 토큰이 올바른지 확인하고 클라이언트에 리소스를 여는 데 동의합니다.
이러한 6단계 중에서 B단계가 핵심입니다. 즉, 사용자가 클라이언트에 권한을 부여하는 방법입니다. 이 권한이 있으면 클라이언트는 토큰을 얻을 수 있으며 토큰으로 리소스를 얻을 수 있습니다. 다음으로 클라이언트가 권한을 얻는 네 가지 모드를 하나씩 설명합니다.
V. 클라이언트의 권한 부여 모드
클라이언트는 토큰 (액세스 토큰)을 얻기 전에 사용자 권한 (권한 부여)을 얻어야 합니다. OAuth 2.0은 다음과 같은 네 가지 권한 부여 방법을 정의합니다.
- 권한 부여 코드 모드 (authorization code)
- 간소화 모드 (implicit)
- 비밀번호 모드 (resource owner password credentials)
- 클라이언트 모드 (client credentials)
VI. 권한 부여 코드 모드
권한 부여 코드 모드 (authorization code)는 권한 부여 모드 중에서 기능이 가장 완벽하고 프로세스가 가장 엄격합니다. 그 특징은 클라이언트의 백그라운드 서버를 통해 "서비스 제공자"의 인증 서버와 상호 작용한다는 것입니다. 구체적인 단계는 다음과 같습니다.
- (A): 사용자가 클라이언트에 액세스하고 클라이언트는 사용자를 인증 서버로 리디렉션합니다.
- (B): 사용자는 클라이언트에 권한을 부여할지 여부를 선택합니다.
- (C): 사용자가 권한을 부여한다고 가정하면 인증 서버는 사용자를 클라이언트가 미리 지정한 "리디렉션 URI" (redirection URI)로 리디렉션하고 동시에 권한 부여 코드를 첨부합니다.
- (D): 클라이언트는 권한 부여 코드를 받고 이전 "리디렉션 URI"를 첨부하여 인증 서버에서 토큰을 신청합니다. 이 단계는 클라이언트의 백그라운드 서버에서 완료되며 사용자에게는 보이지 않습니다.
- (E): 인증 서버는 권한 부여 코드와 리디렉션 URI를 확인합니다. 문제가 없으면 액세스 토큰 (access token)과 새로 고침 토큰 (refresh token)을 클라이언트에 보냅니다.
다음은 위의 단계에 필요한 매개 변수입니다.
-
A단계: 클라이언트가 인증을 신청하는 URI에는 다음 매개 변수가 포함되어 있습니다.
- response_type: 권한 유형을 나타내며 필수 매개 변수이며 여기의 값은 "code"로 고정됩니다.
- client_id: 클라이언트의 ID를 나타내며 필수 매개 변수입니다.
- redirect_uri: 리디렉션 URI를 나타내며 선택적 매개 변수입니다.
- scope: 요청된 권한 범위를 나타내며 선택적 매개 변수입니다.
- state: 클라이언트의 현재 상태를 나타내며 임의의 값을 지정할 수 있습니다. 인증 서버는 이 값을 변경하지 않고 반환합니다.
예:
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1 Host: server.leapcell.io
-
C단계: 서버가 클라이언트에 응답하는 URI에는 다음 매개 변수가 포함되어 있습니다.
- code: 권한 부여 코드를 나타내며 필수 매개 변수입니다. 이 코드는 유효 기간이 매우 짧고 일반적으로 10분으로 설정되며 클라이언트는 이 코드를 한 번만 사용할 수 있으며 그렇지 않으면 인증 서버에서 거부합니다. 이 코드는 클라이언트 ID 및 리디렉션 URI와 일대일로 대응합니다.
- state: 클라이언트 요청에 이 매개 변수가 포함되어 있으면 인증 서버의 응답에도 정확히 동일한 이 매개 변수가 포함되어야 합니다.
예:
HTTP/1.1 302 Found Location: https://client.example.com/cb?code=SplxlqweqwWxSbIA&state=xyz
-
D단계: 클라이언트가 인증 서버에서 토큰을 신청하는 HTTP 요청에는 다음 매개 변수가 포함되어 있습니다.
- grant_type: 사용된 권한 부여 모드를 나타내며 필수 매개 변수이며 여기의 값은 "authorization_code"로 고정됩니다.
- code: 이전 단계에서 얻은 권한 부여 코드를 나타내며 필수 매개 변수입니다.
- redirect_uri: 리디렉션 URI를 나타내며 필수 매개 변수이며 A단계의 이 매개 변수 값과 일치해야 합니다.
- client_id: 클라이언트 ID를 나타내며 필수 매개 변수입니다.
예:
POST /token HTTP/1.1 Host: server.leapcell.io Authorization: Basic czZCaGRrwrqw0M2JW Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
-
E단계: 인증 서버에서 보내는 HTTP 응답에는 다음 매개 변수가 포함되어 있습니다.
- access_token: 액세스 토큰을 나타내며 필수 매개 변수입니다.
- token_type: 토큰 유형을 나타냅니다. 이 값은 대소문자를 구분하지 않으며 필수 매개 변수입니다. 베어러 유형 또는 mac 유형일 수 있습니다.
- expires_in: 만료 시간을 초 단위로 나타냅니다. 이 매개 변수를 생략하면 다른 수단을 통해 만료 시간을 설정해야 합니다.
- refresh_token: 다음 액세스 토큰을 얻는 데 사용되는 새로 고침 토큰을 나타내며 선택적 매개 변수입니다.
- scope: 권한 범위를 나타냅니다. 클라이언트가 요청한 범위와 일치하면 이 항목을 생략할 수 있습니다.
예:
HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token":"2YotnFqwrwqrqwCsicMWpAA", "token_type":"example", "expires_in":3600, "refresh_token":"tGzv3JOqweqweTlKWIA", "example_parameter":"example_value" }
위의 코드에서 관련 매개 변수가 JSON 형식 (Content - Type: application/json)으로 전송되는 것을 알 수 있으며 캐싱이 허용되지 않는다는 것이 HTTP 헤더 정보에 명확하게 지정되어 있습니다.
VII. 간소화 모드
간소화 모드 (implicit grant type)는 타사 응용 프로그램의 서버를 거치지 않고 브라우저에서 인증 서버에서 직접 토큰을 신청하여 "권한 부여 코드" 단계를 건너뛰므로 이름이 지정됩니다. 모든 단계가 브라우저에서 완료되고 토큰이 방문자에게 표시되며 클라이언트를 인증할 필요가 없습니다. 구체적인 단계는 다음과 같습니다.
- (A): 클라이언트는 사용자를 인증 서버로 리디렉션합니다.
- (B): 사용자는 클라이언트에 권한을 부여할지 여부를 결정합니다.
- (C): 사용자가 권한을 부여한다고 가정하면 인증 서버는 사용자에게 클라이언트가 지정한 "리디렉션 URI"로 리디렉션하고 URI의 Hash 부분에 액세스 토큰을 포함합니다.
- (D): 브라우저는 리소스 서버에 요청을 보내며 여기에는 이전 단계에서 받은 Hash 값이 포함되어 있지 않습니다.
- (E): 리소스 서버는 웹 페이지를 반환하고 여기에 포함된 코드는 Hash 값에서 토큰을 얻을 수 있습니다.
- (F): 브라우저는 이전 단계에서 얻은 스크립트를 실행하고 토큰을 추출합니다.
- (G): 브라우저는 토큰을 클라이언트에 보냅니다.
다음은 위의 단계에 필요한 매개 변수입니다.
-
A단계: 클라이언트에서 보내는 HTTP 요청에는 다음 매개 변수가 포함되어 있습니다.
- response_type: 권한 유형을 나타내며 여기의 값은 "token"으로 고정되며 필수 매개 변수입니다.
- client_id: 클라이언트의 ID를 나타내며 필수 매개 변수입니다.
- redirect_uri: 리디렉션 URI를 나타내며 선택적 매개 변수입니다.
- scope: 권한 범위를 나타내며 선택적 매개 변수입니다.
- state: 클라이언트의 현재 상태를 나타내며 임의의 값을 지정할 수 있습니다. 인증 서버는 이 값을 변경하지 않고 반환합니다.
예:
GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1 Host: server.leapcell.io
-
C단계: 인증 서버가 클라이언트에 응답하는 URI에는 다음 매개 변수가 포함되어 있습니다.
- access_token: 액세스 토큰을 나타내며 필수 매개 변수입니다.
- token_type: 토큰 유형을 나타냅니다. 이 값은 대소문자를 구분하지 않으며 필수 매개 변수입니다.
- expires_in: 만료 시간을 초 단위로 나타냅니다. 이 매개 변수를 생략하면 다른 수단을 통해 만료 시간을 설정해야 합니다.
- scope: 권한 범위를 나타냅니다. 클라이언트가 요청한 범위와 일치하면 이 항목을 생략할 수 있습니다.
- state: 클라이언트 요청에 이 매개 변수가 포함되어 있으면 인증 서버의 응답에도 정확히 동일한 이 매개 변수가 포함되어야 합니다.
예:
HTTP/1.1 302 Found Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&token_type=example&expires_in=3600
위의 예에서 인증 서버는 HTTP 헤더 정보의 Location 열을 사용하여 브라우저가 리디렉션하는 URL을 지정합니다. 이 URL의 Hash 부분에 토큰이 포함되어 있습니다. D단계에 따르면 브라우저는 다음 단계에서 Location에 지정된 URL을 방문하지만 Hash 부분은 전송되지 않습니다. 다음 E단계에서 서비스 제공자의 리소스 서버에서 보낸 코드는 Hash에서 토큰을 추출합니다.
VIII. 비밀번호 모드
비밀번호 모드 (Resource Owner Password Credentials Grant)에서 사용자는 자신의 사용자 이름과 비밀번호를 클라이언트에 제공하고 클라이언트는 이 정보를 사용하여 "서비스 제공자"에서 권한을 요청합니다. 이 모드에서는 사용자가 자신의 비밀번호를 클라이언트에 제공해야 하지만 클라이언트는 비밀번호를 저장할 수 없습니다. 일반적으로 사용자가 클라이언트를 매우 신뢰하는 경우에 사용됩니다. 예를 들어 클라이언트가 운영 체제의 일부이거나 유명한 회사에서 제작한 경우입니다. 그리고 인증 서버는 다른 권한 부여 모드를 실행할 수 없는 경우에만 이 모드를 채택하는 것을 고려합니다. 구체적인 단계는 다음과 같습니다.
- (A): 사용자는 사용자 이름과 비밀번호를 클라이언트에 제공합니다.
- (B): 클라이언트는 사용자 이름과 비밀번호를 인증 서버에 보내고 후자에서 토큰을 요청합니다.
- (C): 인증 서버가 문제가 없는지 확인한 후 액세스 토큰을 클라이언트에 제공합니다.
B단계에서 클라이언트에서 보내는 HTTP 요청에는 다음 매개 변수가 포함되어 있습니다.
- grant_type: 권한 유형을 나타내며 여기의 값은 "password"로 고정되며 필수 매개 변수입니다.
- username: 사용자 이름을 나타내며 필수 매개 변수입니다.
- password: 사용자의 비밀번호를 나타내며 필수 매개 변수입니다.
- scope: 권한 범위를 나타내며 선택적 매개 변수입니다.
예:
POST /token HTTP/1.1 Host: server.leapcell.io Authorization: Basic czZCaGRSa3F0gertetewrmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=password&username=johndoe&password=A3ddj3w
C단계에서 인증 서버는 클라이언트에 액세스 토큰을 보냅니다. 예를 들어
HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token":"2YotnFZqweqwreqwrpAA", "token_type":"example", "expires_in":3600, "refresh_token":"tGzv3rewrewrtr2TlKWIA", "example_parameter":"example_value" }
위의 코드에 있는 다양한 매개 변수의 의미는 "권한 부여 코드 모드" 섹션을 참조할 수 있습니다. 전체 과정에서 클라이언트는 사용자의 비밀번호를 저장할 수 없습니다.
IX. 클라이언트 모드
클라이언트 모드 (Client Credentials Grant)는 클라이언트가 사용자 이름이 아닌 자신의 이름으로 "서비스 제공자"와 인증하는 것을 의미합니다. 엄밀히 말하면 클라이언트 모드는 OAuth 프레임워크가 해결해야 할 문제에 속하지 않습니다. 이 모드에서는 사용자가 클라이언트에 직접 등록하고 클라이언트는 자신의 이름으로 "서비스 제공자"에 서비스를 제공하도록 요청합니다. 사실 권한 부여 문제가 없습니다. 구체적인 단계는 다음과 같습니다.
- (A): 클라이언트는 인증 서버로 자신의 ID를 인증하고 액세스 토큰을 요청합니다.
- (B): 인증 서버가 문제가 없는지 확인한 후 액세스 토큰을 클라이언트에 제공합니다.
A단계에서 클라이언트에서 보내는 HTTP 요청에는 다음 매개 변수가 포함되어 있습니다.
- grant_type: 권한 유형을 나타내며 여기의 값은 "client_credentials"로 고정되며 필수 매개 변수입니다.
- scope: 권한 범위를 나타내며 선택적 매개 변수입니다.
예:
POST /token HTTP/1.1 Host: server.leapcell.io Authorization: Basic czZCaGRSqeqwewqmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=client_credentials
인증 서버는 어떤 수단을 통해 클라이언트의 ID를 확인해야 합니다.
B단계에서 인증 서버는 클라이언트에 액세스 토큰을 보냅니다. 예를 들어
HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token":"2Yotnqweqwe1zCsicMWpAA", "token_type":"example", "expires_in":3600, "example_parameter":"example_value" }
위의 코드에 있는 다양한 매개 변수의 의미는 "권한 부여 코드 모드" 섹션을 참조할 수 있습니다.
X. 토큰 업데이트
사용자가 액세스할 때 클라이언트의 "액세스 토큰"이 만료된 경우 "새로 고침 토큰"을 사용하여 새 액세스 토큰을 신청해야 합니다. 토큰을 업데이트하기 위해 클라이언트가 보내는 HTTP 요청에는 다음 매개 변수가 포함되어 있습니다.
- grant_type: 사용된 권한 부여 모드를 나타내며 여기의 값은 "refresh_token"으로 고정되며 필수 매개 변수입니다.
- refresh_token: 이전에 받은 새로 고침 토큰을 나타내며 필수 매개 변수입니다.
- scope: 요청된 권한 범위를 나타내며 이전 응용 프로그램의 범위를 초과할 수 없습니다. 이 매개 변수를 생략하면 이전과 동일함을 의미합니다.
예:
POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzqeqweqwmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
Leapcell: 웹 호스팅, 비동기 작업 및 Redis를 위한 차세대 서버리스 플랫폼
마지막으로 배포를 위한 최고의 플랫폼인 **Leapcell**을 추천합니다.
1. 다국어 지원
- JavaScript, Python, Go 또는 Rust로 개발하세요.
2. 무제한 프로젝트를 무료로 배포
- 사용량에 대해서만 지불하세요. 요청 없음, 요금 없음.
3. 탁월한 비용 효율성
- 사용한 만큼 지불하고 유휴 요금은 없습니다.
- 예: $25는 평균 응답 시간 60ms에서 694만 건의 요청을 지원합니다.
4. 간소화된 개발자 경험
- 간편한 설정을 위한 직관적인 UI.
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
- 실행 가능한 통찰력을 위한 실시간 메트릭 및 로깅.
5. 손쉬운 확장성 및 고성능
- 높은 동시성을 쉽게 처리하기 위한 자동 확장.
- 운영 오버헤드 제로 — 구축에만 집중하세요.
Leapcell Twitter: https://x.com/LeapcellHQ