[OAuth 2.0] 4. Obtaining Authorization(권한 획득)

액세스 토큰 요청을 위해 

  • 클라이언트는 리소스 소유자로부터 권한을 획득 
    • 이 권한은 승인 부여(authorization grant)의 형태로 표현됨
    • 클라이언트는 이를 사용하여 액세스 토큰을 요청
  • OAuth는 4가지 그랜트 유형읠 정의
    • 인가 코드(authorization code)
    • 암묵적(implicit)
    • 리소스 소유자 비밀번호 자격 증명(resource owner apssword credentials)
    • 클라이언트 자격 증명(client credentials)
    • 추가적인 그래트 유형을 정의 가능 -> 확장 메커니즘 제공

4.1 인가 코드 방식

  • 인가 코드 방식은 액세스 토큰 && 리프레시 토큰을 모두 획득하기 위해 사용됨
  • 기밀 클라이언트에 최적화
  • 리다이렉션 기반 흐름 -> 클라이언트는 리소스 소유자의 사용자 에이전트(일반적으로 웹 브라우저)와 상호작용할 수 있어야하며 인가 서버로부터 들어오는 요청을(리다이렉션을 통해) 수신할 수 있어야함

 

(A) - 클라이언트는 리소스 소유자의 사용자 에이전트를 인가 엔드포인트로 안내로 안내하며 흐름 시작 

  • 클라이언트는 인가 요청을 수행할 때, 인가 서버에 자신의 클라이언트 식별자(client_id)와 요청한 접근 범위(scope), 요청과 응답을 연결하기 위한 로컬 상태(local state)를 전달한다. 또한 리소스 소유자가 클라이언트의 접근 요청을 승인하거나 거부한 이후, 해당 결과를 클라이언트에 전달할 수 있도록 인가 서버가 사용자 에이전트를 다시 되돌려 보낼 리다이렉션 URI(redirect_uri)를 함께 포함한다.
GET https://auth.example.com/authorize?
 response_type=code
 &client_id=client123
 &scope=profile%20email
 &state=xyz789
 &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback

 

(B) - 인가 서버는 사용자 에이전트를 통해 리소스 소유자를 인증하고, 리소스 소유자가 클라이언트의 액세스 요청을 승인하는지 또는 거부하는지를 결정한다.

 

(C) - 리소스 소유자가 액세스를 승인한다고 가정하면, 인가 서버는 이전에 제공된(요청 시 또는 클라이언트 등록 시) 리다이렉션 URI를 사용하여 사용자 에이전트를 다시 클라이언트로 리다이렉션한다. 이 리다이렉션 URI에는 인가 코드와, 클라이언트가 이전에 제공한 모든 로컬 상태가 포함된다.

 

(D) - 단계에서 클라이언트는 이전 단계에서 발급받은 인가 코드를 포함하여 인가 서버의 토큰 엔드포인트에 액세스 토큰을 요청한다. 이때 클라이언트는 인가 서버에 대해 자신의 신원을 증명하기 위한 인증 정보를 함께 전달하며, 인증의 검증 주체는 인가 서버이다. 또한 인가 서버가 인가 코드를 발급할 당시 어떤 리다이렉션 URI를 기준으로 인가 코드를 발급했는지를 다시 확인할 수 있도록, 클라이언트는 액세스 토큰을 요청하는 과정에서 인가 코드를 처음 획득할 때 사용했던 리다이렉션 URI와 동일한 값을 요청에 포함하여 전달(redrecit_uri)한다. 이를 통해 인가 서버는 요청을 보낸 클라이언트가 정당한 클라이언트인지, 그리고 인가 코드가 올바른 리다이렉션 URI에 대해 발급된 것인지를 검증한 후 액세스 토큰을 발급한다.

  • 클라이언트가 인가 서버로 보낼 시 
POST /token HTTP/1.1
Host: auth.example.com
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=ABC
&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback

 

(E) - 인가 서버는 클라이언트를 인증하고, 인가 코드를 검증하며, 수신된 리다이렉션 URI가 단계 (C)에서 클라이언트를 리다이렉션하는 데 사용된 URI와 일치하는지를 확인한다. 유효한 경우, 인가 서버는 액세스 토큰을 응답으로 반환하고, 선택적으로 리프레시 토큰을 함께 반환한다.

 

4.1.1 인가 요청

  • 위 그림에서 (A) 단계
  • 클라이언트는 부록 B에 따라 "application/x-www-form-urlencoded" 형식을 사용
  • 인가 엔드포인트 URI의 쿼리 구성 요소(query component)에 다음 매개변수들을 추가함으로써 요청 URI를 구성한다: 부록 B.

 

파라미터 값은 다음과 같다.

  • response_type
    • 필수(REQUIRED). 값은 반드시 "code"로 설정되어야 한다.
  • client_id
    • 필수(REQUIRED). 2.2절에 설명된 클라이언트 식별자.
  • redirect_uri
    • 선택(OPTIONAL). 3.1.2절에 설명된 바와 같다.
  • scope
    • 선택(OPTIONAL). 3.3절에 설명된 액세스 요청의 범위.
  • state
    • 권장하는 값
    • state는 클라이언트가 인가 요청을 시작할 때 생성하는 임의의 값으로, 인가 서버는 이 값을 변경하지 않고 인가 응답에 그대로 포함하여 반환한다. 클라이언트는 반환된 state 값이 자신이 전송한 값과 일치하는지를 확인함으로써, 해당 응답이 자신이 시작한 요청의 결과인지 검증하고, 이를 통해 요청 위조 공격을 방지한다.
    • CRSF 공격은 공격자가 사용자 브라우저를 조작 -> 피해자 모르게 OAuth 요청 발생 -> 인가 서버는 정상 처리 -> 클라이언트는 이게 자기 요청인지 모름 state가 있으면 공격자는 state 값을 예측할 수가 없기에 위조된 요청은 검증에서 걸림 그래서 권장 

클라이언트는 HTTP 리다이렉션 응답을 사용하거나, 사용자 에이전트를 통해 이용 가능한 다른 수단을 사용하여, 리소스 소유자를 구성된 URI로 안내한다.

 

예를 들어, 클라이언트는 사용자 에이전트가 다음과 같은 HTTP 요청을 TLS를 사용하여 수행하도록 안내한다: 

GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
    &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

 

  • 인가 서버는 모든 필수 매개변수가 존재하며 유효한지를 확인하기 위해 요청을 검증
  • 요청이 유효한 경우
    • 인가 서버는 리소스 소유자를 인증하고, 리소스 소유자에게 요청을 묻거나 다른 수단을 통해 승인 여부를 확정함으로써 인가 결정을 획득한다. 결정이 확정되면, 인가 서버는 HTTP 리다이렉션 응답을 사용하거나 사용자 에이전트를 통해 이용 가능한 다른 수단을 사용하여, 사용자 에이전트를 제공된 클라이언트 리다이렉션 URI로 안내한다.

 

4.1.2 인가 응답

  • 리소스 소유자가 액세스 요청을 승인한 경우, 인가 서버는 인가 코드를 발급하고, 부록 B에 따라 "application/x-www-form-urlencoded" 형식을 사용하여 리다이렉션 URI의 쿼리 구성 요소에 다음 매개변수들을 추가함으로써 이를 인가서버는 클라이언트에 전달한다:

파라미터는 다음과 같다.

  • code
    • 필수(REQUIRED).
    • 인가 서버에 의해 생성된 인가 코드. 인가 코드는 유출 위험을 완화하기 위해 발급된 후 짧은 시간 내에 만료되어야 한다(MUST).
    • 인가 코드의 최대 유효 기간은 10분을 권장.
    • 클라이언트는 인가 코드를 두 번 이상 사용해서는 안 된다(MUST NOT). 인가 코드가 두 번 이상 사용될 경우, 인가 서버는 요청을 반드시 거부해야 하며(MUST) 또한, 이미 해당 인가 코드를 기반으로 발급된 액세스 토큰이나 리프레시 토큰 역시 안전하지 않다고 판단하여 모두 철회하는 것이 바람직하다.(SHOULD). 인가 코드는 클라이언트 식별자와 리다이렉션 URI에 바인딩된다.
  • state
    • 클라이언트 인가 요청에 "state" 매개변수가 존재했던 경우 필수 (REQUIRED). 클라이언트로부터 수신한 정확한 값.

 

예를 들어, 인가 서버는 다음과 같은 HTTP 응답을 전송함으로써 사용자 에이전트를 리다이렉션한다:

HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
          &state=xyz
  • 클라이언트는 인식하지 못하는 응답 매개변수들을 무시해야 한다(MUST).
  • 인가 코드 문자열의 크기는 본 명세에서 정의되지 않는다.
  • 클라이언트는 코드 값의 크기에 대해 가정을 해서는 안 된다.
  • 인가 서버는 자신이 발급하는 모든 값의 크기를 문서화하는 것이 바람직하다(SHOULD).

 

4.1.2.1 오류 응답

  • redirect_uri -> 누락, 유효 x, 등록된 값과 일치 x, client_id -> 누락 및 유효 x\
    • 인가 서버는 리소스 소유자에게 오류를 알려야 하며(SHOULD),
    • 유효하지 않은 리다이렉션 URI로 사용자 에이전트를 자동으로 리다이렉션해서는 안 된다(MUST NOT).
  • redirect_uri는 정상이지만 요청 자체가 실패한 경우
    • 리소스 소유자가 접근 요청을 거부 
    • scope가 잘못됨 
    • response_type 지원 X
    • 서버 오류 등
    • 인가 서버는 부록 B에 따라 "application/x-www-form-urlencoded" 형식을 사용하여 리다이렉션 URI의 쿼리 구성 요소에 다음 매개변수들을 추가함으로써 클라이언트에 이를 알린다: 
요청은 처리 했지만 이런 이유로 실패했다가 error

 

error 필수(REQUIRED). 다음 중 하나의 단일 ASCII [USASCII] 오류 코드:

  • invalid_request
    • 요청에 필수 매개변수가 누락되었거나, 유효하지 않은 매개변수 값이 포함되었거나, 매개변수가 두 번 이상 포함되었거나, 그 외의 방식으로 잘못 형성된 경우.
  • unauthorized_client
    • 클라이언트가 이 방법을 사용하여 인가 코드를 요청할 수 있도록 인가되지 않은 경우.
  • access_denied
    • 리소스 소유자 또는 인가 서버가 요청을 거부한 경우.
  • unsupported_response_type
    • 인가 서버가 이 방법을 사용한 인가 코드 획득을 지원하지 않는 경우.
  • invalid_scope
    • 요청된 범위(scope)가 유효하지 않거나, 알 수 없거나, 잘못 형성된 경우.
  • server_error
    • 인가 서버가 요청을 처리하는 것을 방해하는 예기치 않은 상태를 만난 경우. (이 오류 코드는 HTTP 리다이렉션을 통해 클라이언트에 500 Internal Server Error 상태 코드를 반환할 수 없기 때문에 필요하다.)
  • temporarily_unavailable
    • 서버의 일시적인 과부하 또는 유지보수로 인해 인가 서버가 현재 요청을 처리할 수 없는 경우.(이 오류 코드는 HTTP 리다이렉션을 통해 클라이언트에 503 Service Unavailable 상태 코드를 반환할 수 없기 때문에 필요하다.)

 

error 매개변수의 값은 URL에 안전하게 포함될 수 있는 ASCII 문자만 사용해야 하며, 제어 문자나 특수 문자, 유니코드 문자는 포함해서는 안 된다.

 

다음 error_description과 error_uri를 추가적으로 보낼 수 있다.

  • error_description
    • 선택(OPTIONAL). 발생한 오류를 이해하는 데 도움을 주기 위해 클라이언트 개발자를 보조하는, 사람이 읽을 수 있는 ASCII [USASCII] 텍스트 형태의 추가 정보. "error_description" 매개변수의 값에는 %x20-21 / %x23-5B / %x5D-7E 집합에 포함되지 않은 문자가 포함되어서는 안 된다(MUST NOT).
  • error_uri
    • 선택(OPTIONAL). 오류에 대한 정보를 포함한 사람이 읽을 수 있는 웹 페이지를 식별하는 URI로, 클라이언트 개발자에게 오류에 대한 추가 정보를 제공하는 데 사용된다. "error_uri" 매개변수의 값은 URI-reference 구문을 따라야 하며, 따라서 %x21 / %x23-5B / %x5D-7E 집합에 포함되지 않은 문자를 포함해서는 안 된다(MUST NOT).
  • state
    • 클라이언트 인가 요청에 "state" 매개변수가 존재했던 경우 필수 (REQUIRED). 클라이언트로부터 수신한 정확한 값.

예시

HTTP/1.1 302 Found
Location: https://client.example.com/cb?error=access_denied&state=xyz

 

 

4.1.3 액세스 토큰 요청

클라이언트는 부록 B에 따라 "application/x-www-form-urlencoded" 형식을 사용하고, HTTP 요청 엔터티 본문(entity-body)에 UTF-8 문자 인코딩을 적용하여, 다음 매개변수들을 전송함으로써 토큰 엔드포인트에 요청을 보낸다:

 

  • grant_type
    • 필수(REQUIRED). 값은 반드시 "authorization_code"로 설정되어야 한다.
  • code
    • 필수(REQUIRED). 인가 서버로부터 수신한 인가 코드.
  • redirect_uri
    • 4.1.1절에 설명된 바와 같이, 인가 요청에 "redirect_uri" 매개변수가 포함되었던 경우 필수(REQUIRED)이며, 그 값은 반드시 동일해야 한다.
  • client_id
    • 3.2.1절에 설명된 바와 같이 클라이언트가 인가 서버에 대해 인증을 수행하지 않는 경우 필수(REQUIRED).

액세스 토큰을 요청할 때 

  • 기밀 클라이언트
  • 클라이언트 자격 증명이 발급되었거나(또는 다른 인증 요구 사항이 할당된 경우)

3.2.1절에 설명된 바와 같이 인가 서버에 대해 반드시 인증을 수행해야 한다(MUST).

 

예를 들어, 클라이언트는 TLS를 사용하여 다음과 같은 HTTP 요청을 보낸다(가독성을 위해 추가적인 줄 바꿈이 포함되어 있음):

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW // base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

 

인가 서버는 다음을 반드시 수행해야 한다(MUST):

  • 기밀 클라이언트이거나 클라이언트 자격 증명이 발급된 모든 클라이언트(또는 다른 인증 요구 사항이 있는 클라이언트)에 대해 클라이언트 인증을 요구해야 한다.
  • 클라이언트 인증이 포함된 경우, 클라이언트를 인증해야 한다.
  • 인가 코드가 인증된 기밀 클라이언트에 발급된 것인지 확인하거나, 클라이언트가 공개 클라이언트인 경우 요청에 포함된 "client_id"에 발급된 코드인지 확인해야 한다.
  • 인가 코드가 유효한지 검증해야 한다.
  • 4.1.1절에 설명된 초기 인가 요청에 "redirect_uri" 매개변수가 포함되어 있었던 경우, "redirect_uri" 매개변수가 존재하는지 확인해야 하며, 포함된 경우 그 값이 반드시 동일한지 확인해야 한다.

 

4.1.4 액세스 토큰 응답

액세스 토큰 요청이 유효하고 인가된 경우, 인가 서버는 5.1절에 설명된 바와 같이 액세스 토큰과 선택적인 리프레시 토큰을 발급한다. 요청 클라이언트 인증이 실패했거나 유효하지 않은 경우, 인가 서버는 5.2절에 설명된 바와 같이 오류 응답을 반환한다.

 

성공적인 응답의 예는 다음과 같다:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter":"example_value"
}

 

4.2 암묵적 방식

암묵적 방식은 액세스 토큰을 획득하기 위해 사용되며(리프레시 토큰 발급은 지원하지 않는다), 특정 리다이렉션 URI에서 동작하는 것으로 알려진 공개 클라이언트(public clients)에 최적화되어 있다. 이러한 클라이언트는 일반적으로 JavaScript와 같은 스크립트 언어를 사용하여 브라우저에서 구현된다.

 

이 방식은 리다이렉션 기반 흐름이므로, 클라이언트는 리소스 소유자의 사용자 에이전트(일반적으로 웹 브라우저)와 상호작용할 수 있어야 하며, 인가 서버로부터의 들어오는 요청을(리다이렉션을 통해) 수신할 수 있어야 한다.

 

인가 코드 방식과 달리, 클라이언트가 인가 요청과 액세스 토큰 요청을 별도로 수행하는 대신, 클라이언트는 인가 요청의 결과로 액세스 토큰을 직접 수신한다.

 

암묵적 방식에는 클라이언트 인증이 포함되지 않으며, 리소스 소유자의 존재와 리다이렉션 URI의 등록에 의존한다. 액세스 토큰이 리다이렉션 URI에 인코딩되기 때문에, 동일한 장치에 존재하는 리소스 소유자 및 다른 애플리케이션에 노출될 수 있다.

 

참고: 단계 (A)와 (B)를 나타내는 선들은 사용자 에이전트를 통과하면서 두 부분으로 나뉘어 표시되어 있다.

 

(A) - 단계에서 클라이언트는 사용자 에이전트를 인가 서버의 인가 엔드포인트로 리다이렉션하여 인가 요청을 시작한다. 이 요청에는 클라이언트 식별자(client_id), 요청한 접근 범위(scope), 상태 값(state), 그리고 인가 결과를 반환받을 리다이렉션 URI가 포함되며(redirect_uri), 암묵적 방식의 특성상 response_type token으로 설정된다. 이를 통해 클라이언트는 인가 요청의 결과로 액세스 토큰을 직접 전달받을 준비를 한다.

 

(B) - 인가 서버는 사용자 에이전트를 통해 리소스 소유자를 인증하고, 리소스 소유자가 클라이언트의 액세스 요청을 승인하는지 또는 거부하는지를 결정한다.

 

(C) - 리소스 소유자가 액세스를 승인한다고 가정하면, 인가 서버는 이전에 제공된 리다이렉션 URI를 사용하여 사용자 에이전트를 다시 클라이언트로 리다이렉션한다. 이 리다이렉션 URI에는 URI 프래그먼트(fragment)에 액세스 토큰이 포함된다.

 

(D) - 사용자 에이전트는 리다이렉션 지시에 따라 웹에 호스팅된 클라이언트 리소스에 요청을 보낸다(이 요청에는 [RFC2616]에 따라 프래그먼트가 포함되지 않는다). 사용자 에이전트는 프래그먼트 정보를 로컬에 유지한다.

 

(E) - 웹에 호스팅된 클라이언트 리소스는 사용자 에이전트가 유지하고 있는 프래그먼트를 포함한 전체 리다이렉션 URI에 접근할 수 있고, 프래그먼트에 포함된 액세스 토큰(및 기타 매개변수)을 추출할 수 있는 웹 페이지(일반적으로 스크립트가 포함된 HTML 문서)를 반환한다.

 

(F) - 사용자 에이전트는 웹에 호스팅된 클라이언트 리소스가 제공한 스크립트를 로컬에서 실행하여, 액세스 토큰을 추출한다.

 

(G) - 사용자 에이전트는 액세스 토큰을 클라이언트에 전달한다.

 

암묵적 방식 사용에 대한 배경은 1.3.2절과 9절을 참조한다. 암묵적 방식을 사용할 때의 중요한 보안 고려 사항은 10.3절과 10.16절을 참조한다.

 

예시를 통해 한 번 보자
- 클라이언트: SPA 웹 앱 https://client.example.com
- 인가 서버: https://auth.example.com
- 리소스 소유자: 사용자 홍길동
- 목표: 브라우저 JS 앱이 액세스 토큰을 직접 얻기
- 방식: 암묵적 방식 (Implicit Grant)

(A) 인가 요청 시작 - 브라우저 리다이렉션 

클라이언트가 하는 일 

클라이언트는 브라우저를 아래 URL로 이동시킨다.
GET https://auth.example.com/authorize?
 response_type=token
 &client_id=client123
 &scope=profile email
 &state=abc123
 &redirect_uri=https://client.example.com/callback​

 

-> 이 사용자에게 이 권한을 달라는 요청 시작

(B) 사용자 인증 & 동의
인가 서버가 하는 일 
- 로그인 화면 표시
- 사용자 인증(ID/PW)
- 권한 동의 화면 표시
이 앱이 다음 권한을 요청합니다:
- 프로필
- 이메일

[허용]   [거부]​
사용자 -> 허용 클릭

(C) 액세슨 토큰을 담아 리다이렉션
인가 서버가 하는 일

인가 서버는 redirect_uri로 다시 브라우저를 돌려보냄 -> 액세스 토큰을 URI 프래그먼트(#)에 포함
HTTP/1.1 302 Found
Location: https://client.example.com/callback#
 access_token=AAA.BBB.CCC
 &token_type=Bearer
 &expires_in=3600
 &state=abc123​

(D) 브라우저가 웹에 호스팅 된 클라이언트 리소스에 요청 
브라우저의 실제 HTTP 요청 -> fragment(#…)는 포함되지 않는다 -> 이게 자동으로 요청이 보내질 때 프래그먼트가 빠짐 즉, 서버로 HTTP 요청을 보낼 때
GET /callback HTTP/1.1
Host: client.example.com​

이 때 상태 브라우저 주소창에는

https://client.example.com/callback#access_token=AAA.BBB.CCC

- 서버는 토큰을 모름
- 브라우저만 알고 있는 상태 


(E) 웹 호스팅된 클라이언트 리소스(React 같은 SPA의 HTML + JS라고 보면 됨)가 HTML + JS 반환
서버가 응답
<!DOCTYPE html>
<html>
<body>
<script>
  // 여기서 fragment를 읽을 준비
</script>
</body>
</html>​
-> 이 HTML은 브라우저에서 실행될 JS를 포함

(F) 브라우저에서 스크립트 실행
JS가 하는 일 
// 브라우저 주소창의 fragment 접근
const hash = window.location.hash;

// #access_token=AAA.BBB.CCC&token_type=Bearer...
// 파싱해서 토큰 추출
const token = extractAccessToken(hash);​

 

-> 이 단계에서 액세스 토큰을 실제로 얻는 주체는 브라우저 JS

(G) 토큰을 클라이언트 로직에서 사용
이후 동작 예
fetch("https://api.example.com/user", {
  headers: {
    Authorization: "Bearer " + token
  }
});​
또는
메모리에 저장, JS 변수로 유지, 로컬 스토리지(보안 위험)

 

4.2.1 인가 요청

  • 단계 (A)를 말함

클라이언트는 부록 B에 따라 "application/x-www-form-urlencoded" 형식을 사용하여, 인가 엔드포인트 URI의 쿼리 구성 요소에 다음 매개변수들을 추가함으로써 요청 URI를 구성

 

  • response_type
    • 필수(REQUIRED). 값은 반드시 "token"으로 설정되어야 한다.
  • client_id
    • 필수(REQUIRED). 2.2절에 설명된 클라이언트 식별자.
  • redirect_uri
    • 선택(OPTIONAL). 3.1.2절에 설명된 바와 같다.
  • scope
    • 선택(OPTIONAL). 3.3절에 설명된 액세스 요청의 범위.
  • state
    • 권장(RECOMMENDED). 요청과 콜백 사이에서 상태를 유지하기 위해 클라이언트가 사용하는 불투명한 값. 인가 서버는 사용자 에이전트를 다시 클라이언트로 리다이렉션할 때 이 값을 포함한다. 이 매개변수는 10.12절에 설명된 교차 사이트 요청 위조(cross-site request forgery) 를 방지하기 위해 사용되어야 한다(SHOULD).

클라이언트는 HTTP 리다이렉션 응답을 사용하거나, 사용자 에이전트를 통해 이용 가능한 다른 수단을 사용하여, 리소스 소유자를 구성된 URI로 안내한다.

 

예를 들어, 클라이언트는 사용자 에이전트가 다음과 같은 HTTP 요청을 TLS를 사용하여 수행하도록 안내한다(가독성을 위해 추가적인 줄 바꿈이 포함되어 있음):

GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
    &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

 

인가 서버는 모든 필수 매개변수가 존재하며 유효한지를 확인하기 위해 요청을 검증한다. 인가 서버는 3.1.2절에 설명된 바와 같이, 액세스 토큰을 리다이렉션할 대상이 되는 리다이렉션 URI가 클라이언트에 의해 등록된 리다이렉션 URI와 일치하는지를 반드시 검증해야 한다(MUST).

 

요청이 유효한 경우, 인가 서버는 리소스 소유자를 인증하고, 리소스 소유자에게 요청을 묻거나 다른 수단을 통해 승인 여부를 확정함으로써 인가 결정을 획득한다.

 

결정이 확정되면, 인가 서버는 HTTP 리다이렉션 응답을 사용하거나 사용자 에이전트를 통해 이용 가능한 다른 수단을 사용하여, 사용자 에이전트를 제공된 클라이언트 리다이렉션 URI로 안내한다.

 

4.2.2 액세스 토큰 응답

리소스 소유자가 액세스 요청을 승인한 경우, 인가 서버는 부록 B에 따라 "application/x-www-form-urlencoded" 형식을 사용하여 리다이렉션 URI의 프래그먼트 구성 요소에 다음 매개변수들을 추가함으로써 액세스 토큰을 발급하고 이를 클라이언트[사용자 에이전트 여기서 말하는 클라이언트는 최종임]에 전달한다:

 

  • access_token
    • 필수(REQUIRED). 인가 서버에 의해 발급된 액세스 토큰.
  • token_type
    • 필수(REQUIRED). 7.1절에 설명된 바와 같이 발급된 토큰의 유형. 값은 대소문자를 구분하지 않는다.
  • expires_in
    • 권장(RECOMMENDED). 액세스 토큰의 수명(초 단위). 예를 들어, 값 "3600"은 응답이 생성된 시점으로부터 액세스 토큰이 1시간 후에 만료됨을 의미한다. 생략된 경우, 인가 서버는 다른 수단을 통해 만료 시간을 제공하거나 기본값을 문서화하는 것이 바람직하다(SHOULD).
  • scope
    • 클라이언트가 요청한 범위(scope)와 동일한 경우 선택(OPTIONAL), 그렇지 않은 경우 필수(REQUIRED). 3.3절에 설명된 액세스 토큰의 범위.
  • state
    • 클라이언트 인가 요청에 "state" 매개변수가 존재했던 경우 필수 (REQUIRED). 클라이언트로부터 수신한 정확한 값.

 

인가 서버는 리프레시 토큰을 발급해서는 안 된다(MUST NOT).

 

예를 들어, 인가 서버는 다음과 같은 HTTP 응답을 전송함으로써 사용자 에이전트를 리다이렉션한다(가독성을 위해 추가적인 줄 바꿈이 포함되어 있음):

HTTP/1.1 302 Found
Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
          &state=xyz&token_type=example&expires_in=3600

 

일브 사용자 에이전트(브라우저)는 HTTP 302 Location 헤더에 #fragment가 들어 있는 리다이렉션을 제대로 처리하지 못한다 그래서 3xx 리다이렉션을 쓰지 말고 HTML 페이지를 직접 반환

즉, 인가 서버는 다음과 같이 응답

HTTP/1.1 200 OK
Content-Type: text/html

<html>
<body>
  <p>로그인이 완료되었습니다.</p>
  <a href="https://client.example.com/callback#access_token=AAA.BBB.CCC">
    Continue
  </a>
</body>
</html>

사용자가 버튼을 클릭하면 

- fragment 포함된 URL로 이동

- 토큰 전달 성공

 

클라이언트(사용자 에이전트[브라우저] 안에서 실행되는 클라이언트 애플리케이션(JS코드))는 인식하지 못하는 응답 매개변수들을 무시해야 한다(MUST). 액세스 토큰 문자열의 크기는 본 명세에서 정의되지 않는다. 클라이언트는 값의 크기에 대해 가정을 해서는 안 된다. 인가 서버는 자신이 발급하는 모든 값의 크기를 문서화하는 것이 바람직하다(SHOULD).

 

4.2.2.1 오류 응답

리다이렉션 URI가 누락되었거나, 유효하지 않거나, 일치하지 않는 경우, 또는 클라이언트 식별자가 누락되었거나 유효하지 않은 경우로 인해 요청이 실패하면, 인가 서버는 리소스 소유자에게 오류를 알려야 하며(SHOULD), 유효하지 않은 리다이렉션 URI로 사용자 에이전트를 자동으로 리다이렉션해서는 안 된다(MUST NOT).

 

리소스 소유자가 액세스 요청을 거부한 경우, 또는 리다이렉션 URI의 누락이나 유효하지 않음 이외의 이유로 요청이 실패한 경우, 인가 서버는 부록 B에 따라 "application/x-www-form-urlencoded" 형식을 사용하여 리다이렉션 URI의 프래그먼트 구성 요소에 다음 매개변수들을 추가함으로써 클라이언트에 이를 알린다:

 

error 필수(REQUIRED)다음 중 하나의 단일 ASCII [USASCII] 오류 코드:

 

  • invalid_request
    • 요청에 필수 매개변수가 누락되었거나, 유효하지 않은 매개변수 값이 포함되었거나, 매개변수가 두 번 이상 포함되었거나, 그 외의 방식으로 잘못 형성된 경우.
  • unauthorized_client
    • 클라이언트가 이 방법을 사용하여 액세스 토큰을 요청할 수 있도록 인가되지 않은 경우.
  • access_denied
    • 리소스 소유자 또는 인가 서버가 요청을 거부한 경우.
  • unsupported_response_type
    • 인가 서버가 이 방법을 사용한 액세스 토큰 획득을 지원하지 않는 경우.
  • invalid_scope
    • 요청된 범위(scope)가 유효하지 않거나, 알 수 없거나, 잘못 형성된 경우.
  • server_error
    • 인가 서버가 요청을 처리하는 것을 방해하는 예기치 않은 상태를 만난 경우. (이 오류 코드는 HTTP 리다이렉션을 통해 클라이언트에 500 Internal Server Error 상태 코드를 반환할 수 없기 때문에 필요하다.)
  • temporarily_unavailable
    • 서버의 일시적인 과부하 또는 유지보수로 인해 인가 서버가 현재 요청을 처리할 수 없는 경우. (이 오류 코드는 HTTP 리다이렉션을 통해 클라이언트에 503 Service Unavailable 상태 코드를 반환할 수 없기 때문에 필요하다.)

"error" 매개변수의 값에는 %x20-21 / %x23-5B / %x5D-7E 집합에 포함되지 않은 문자가 포함되어서는 안 된다(MUST NOT).

 

4.3 리소스 소유자 비밀번호 자격 증명 방식

리소스 소유자 비밀번호 자격 증명 방식은 장치 운영체제나 고도의 권한을 가진 애플리케이션과 같이, 리소스 소유자가 클라이언트와 신뢰 관계를 가지고 있는 경우에 적합하다. 인가 서버는 이 그랜트 유형을 활성화할 때 특별한 주의를 기울여야 하며, 다른 흐름들이 적합하지 않은 경우에만 이를 허용해야 한다.

 

이 그랜트 유형은 리소스 소유자의 자격 증명(일반적으로 대화형 폼을 통해 입력되는 사용자 이름과 비밀번호)을 획득할 수 있는 클라이언트에 적합하다. 또한 이 방식은 HTTP Basic 또는 Digest 인증과 같은 직접 인증 방식을 사용하는 기존 클라이언트를, 저장된 자격 증명을 액세스 토큰으로 변환함으로써 OAuth로 마이그레이션하는 데에도 사용된다.

HTTP Basic 또는 Digest 인증과 같은 직접 인증 방식을 사용하는 기존 클라이언트를, 저장된 자격 증명을 액세스 토큰으로 변환함으로써 OAuth로 마이그레이션하는 데에도 사용된다.

-> 이 말이 
1. 직접 인증 방식
OAuth 이전의 전통적인 방식들 :
HTTP Basic 인증
Authorization: Basic base64(username:password)​

HTTP Digest 인증
- 비밀번호 기반 챌린지-응답 방식
- 어쩃뜬 서버가 비밀번호 검증

이것들이 클라이언트가 사용자 비밀번호를 직접 들고 있음, 매 요청 마다 비밀번호 기반 인증, 권한 분리, 범위(scope), 토큰 만료 개념이 없음 

2. 기존 클라이언트
- 오래된 모바일 앱
- 레거시 데스크톱 프로그램
- 사내 시스템
- 이미 배포되어 있고, 구조를 크게 못 바꾸는 앱

클라이언트 내부에
username / password 저장
↓
API 호출할 때마다 서버로 전송

이 구조였음

그런데 OAuth로 가고 싶다?
- 비밀번호 직접 전달 x -> 액세스 토큰 사용 하지만 기존 클라이언트 한 번에 고치기 어려우니 마이그레이션용 임시 해법 == 리소스 소유자 비밀번호 자격 증명 방식

 

(A) - 리소스 소유자는 자신의 사용자 이름과 비밀번호를 클라이언트에 제공한다.

 

(B) - 클라이언트는 리소스 소유자로부터 수신한 자격 증명을 포함하여 인가 서버의 토큰 엔드포인트에 액세스 토큰을 요청한다. 이 요청을 수행할 때, 클라이언트는 인가 서버에 대해 인증을 수행한다.

 

(C) - 인가 서버는 클라이언트를 인증하고 리소스 소유자의 자격 증명을 검증하며, 유효한 경우 액세스 토큰을 발급한다.

 

4.3.1 인가 요청 및 응답

클라이언트가 리소스 소유자의 자격 증명을 획득하는 방식은 본 명세의 범위를 벗어난다. 클라이언트는 액세스 토큰을 획득한 이후에는 해당 자격 증명을 반드시 폐기해야 한다(MUST).

 

4.3.2 액세스 토큰 요청

클라이언트는 부록 B에 따라 "application/x-www-form-urlencoded" 형식을 사용하고, HTTP 요청 엔터티 본문(entity-body)에 UTF-8 문자 인코딩을 적용하여, 다음 매개변수들을 추가함으로써 토큰 엔드포인트에 요청을 보낸다:

 

  • grant_type
    • 필수(REQUIRED). 값은 반드시 "password"로 설정되어야 한다.
  • username
    • 필수(REQUIRED). 리소스 소유자의 사용자 이름.
  • password
    • 필수(REQUIRED). 리소스 소유자의 비밀번호.
  • scope
    • 선택(OPTIONAL). 3.3절에 설명된 액세스 요청의 범위.

클라이언트 유형이 기밀 클라이언트이거나, 클라이언트 자격 증명이 발급되었거나(또는 다른 인증 요구 사항이 할당된 경우), 클라이언트는 3.2.1절에 설명된 바와 같이 인가 서버에 대해 반드시 인증을 수행해야 한다(MUST).

 

예를 들어, 클라이언트는 전송 계층 보안(transport-layer security)을 사용하여 다음과 같은 HTTP 요청을 보낸다(가독성을 위해 추가적인 줄 바꿈이 포함되어 있음):

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=johndoe&password=A3ddj3w

 

인가 서버는 다음을 반드시 수행해야 한다(MUST):

  • 기밀 클라이언트이거나 클라이언트 자격 증명이 발급된 모든 클라이언트(또는 다른 인증 요구 사항이 있는 클라이언트)에 대해 클라이언트 인증을 요구해야 한다.
  • 클라이언트 인증이 포함된 경우, 클라이언트를 인증해야 한다.
  • 기존의 비밀번호 검증 알고리즘을 사용하여 리소스 소유자의 비밀번호 자격 증명을 검증해야 한다.

이 액세스 토큰 요청은 리소스 소유자의 비밀번호를 사용하므로, 인가 서버는 무차별 대입 공격(brute force attack)에 대해 엔드포인트를 반드시 보호해야 한다(MUST). (예: 요청 속도 제한(rate-limitation) 적용 또는 경고 생성)

무차별 대입 공격(brute force attack)란?
- 비밀번호, 인증 토큰, 암호 키등을 가능한 모든 조합으로 반복 시도하여 알아내려는 공격

 

4.3.3 액세스 토큰 응답

액세스 토큰 요청이 유효하고 인가된 경우, 인가 서버는 5.1절에 설명된 바와 같이 액세스 토큰과 선택적인 리프레시 토큰을 발급한다. 요청에서 클라이언트 인증이 실패했거나 요청이 유효하지 않은 경우, 인가 서버는 5.2절에 설명된 바와 같이 오류 응답을 반환한다.

 

성공적인 응답의 예는 다음과 같다:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter":"example_value"
}

 

4.4 클라이언트 자격 증명 방식

클라이언트는 보호된 리소스가 자신의 제어 하에 있거나, 또는 인가 서버와 사전에 합의된 다른 리소스 소유자의 리소스에 접근을 요청하는 경우(그 방법은 본 명세의 범위를 벗어난다), 자신의 클라이언트 자격 증명(또는 기타 지원되는 인증 수단)만을 사용하여 액세스 토큰을 요청할 수 있다.

이 방식은 사용자 없이 클라이언트(서버) 자기 권한으로 API를 호출 할 때 쓰는 방식. 

보호된 리소스가 자신의 제어 하에 있거
- 클라이언트가 접근하려는 리소스(API)가 자기 회사/자기 서비스가 관리하는 리소스
ex -> 백엔드 서버 A → 같은 회사의 백엔드 서버 B 배치 서버 → 내부 관리 API 마이크로서비스 A → 마이크로서비스 B

또는 인가 서버와 사전에 합의된 다른 시소스 소유자와 리소스
- 리소스가 남의 것이긴 한데 
- 인가 서버와 사전에 계약/합의가 되어있는 경우
ex -> 결제 대행사 API, 물류 회사 API

자신의 클라이언트 자격 증명만을 사용하여 
- client_id, client_secret를 이용해서 자기 신분증으로 로그인

 

클라이언트 자격 증명 방식은 반드시 기밀 클라이언트에서만 사용되어야 한다(MUST).

 

 

(A) - 클라이언트는 인가 서버에 대해 인증을 수행하고, 토큰 엔드포인트에 액세스 토큰을 요청한다.

 

(B) - 인가 서버는 클라이언트를 인증하고, 유효한 경우 액세스 토큰을 발급한다.

 

4.4.1 인가 요청 및 응답

클라이언트 인증이 인가 그랜트로 사용되므로, 추가적인 인가 요청은 필요하지 않다.

 

4.4.2 액세스 토큰 요청

클라이언트는 부록 B에 따라 "application/x-www-form-urlencoded" 형식을 사용하고, HTTP 요청 엔터티 본문(entity-body)에 UTF-8 문자 인코딩을 적용하여, 다음 매개변수들을 추가함으로써 토큰 엔드포인트에 요청을 보낸다:

  • grant_type
    • 필수(REQUIRED). 값은 반드시 "client_credentials"로 설정되어야 한다.
  • scope
    • 선택(OPTIONAL). 3.3절에 설명된 액세스 요청의 범위.

클라이언트는 3.2.1절에 설명된 바와 같이 인가 서버에 대해 반드시 인증을 수행해야 한다(MUST).

 

예를 들어, 클라이언트는 전송 계층 보안(transport-layer security)을 사용하여 다음과 같은 HTTP 요청을 보낸다(가독성을 위해 추가적인 줄 바꿈이 포함되어 있음):

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials

 

인가 서버는 클라이언트를 반드시 인증해야 한다(MUST).

 

4.3.3 액세스 토큰 응답

액세스 토큰 요청이 유효하고 인가된 경우, 인가 서버는 5.1절에 설명된 바와 같이 액세스 토큰을 발급한다. 리프레시 토큰은 포함되지 않는 것이 바람직하다(SHOULD NOT). 요청에서 클라이언트 인증이 실패했거나 요청이 유효하지 않은 경우, 인가 서버는 5.2절에 설명된 바와 같이 오류 응답을 반환한다.

 

성공적인 응답의 예는 다음과 같다:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "example_parameter":"example_value"
}

 

4.5 확장 승인

클라이언트는 토큰 엔드포인트의 "grant_type" 매개변수 값으로 인가 서버에 의해 정의된 절대 URI를 지정하고, 필요한 추가 매개변수들을 더함으로써 확장 그랜트 유형을 사용한다.

 

예를 들어, [OAuth-SAML2]에 정의된 보안 어설션 마크업 언어(Security Assertion Markup Language, SAML) 2.0 어설션 그랜트 유형을 사용하여 액세스 토큰을 요청하기 위해, 클라이언트는 TLS를 사용하여 다음과 같은 HTTP 요청을 보낼 수 있다(가독성을 위해 추가적인 줄 바꿈이 포함되어 있음):

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Asaml2-
bearer&assertion=PEFzc2VydGlvbiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDU
[...간결성을 위해 생략됨...]aG5TdGF0ZW1lbnQ-PC9Bc3NlcnRpb24-

액세스 토큰 요청이 유효하고 인가된 경우, 인가 서버는 5.1절에 설명된 바와 같이 액세스 토큰과 선택적인 리프레시 토큰을 발급한다. 요청에서 클라이언트 인증이 실패했거나 요청이 유효하지 않은 경우, 인가 서버는 5.2절에 설명된 바와 같이 오류 응답을 반환한다.

'RFC > OAuth 2.0' 카테고리의 다른 글

[OAuth 2.0] 6. Refreshing an Access Token  (0) 2026.01.13
[OAuth 2.0] 5. Issuing an Access Token  (0) 2026.01.13
[OAuth 2.0] 3. Protocol Endpoints  (0) 2025.12.31
[OAuth 2.0] 2. Client Registration  (0) 2025.12.26
[OAuth 2.0] 1. Introduction  (2) 2025.12.20