[OAuth 2.0] 3. Protocol Endpoints

인가 과정은 두 개의 인가 서버 엔드포인트(HTTP 리소스) 사용

  • Authorization endpoint(인가 엔드포인트)
    • 클라이언트가 직접 사용자에게 권한을 요구하는 것이 아님. 대신 클라이언트는 사용자 에이전트(보통 웹 프라우저)를 인가 서버의 이 엔드포인트로 리다이렉션시킨다. 
    • 이를 통해 리소스 소유자는 인가 서버에 로그인 및 클라이언트가 요청한 권한에 대해 동의하거나 거부 
    • 즉, 이 엔드포인트는 사용자(리소스 소유자)와 인가 서버가 직접 상호작용하면서 인가를 결정하는 장소이고 클라이언트는 사용자 에이전트를 경유해서 인가를 요청할 뿐
  • Token endPoint(토큰 엔드포인트)
    • 클라이언트가 인가서버와 직접 통신하며 사용되는 엔드포인트
    • 클라이언트는 앞 단계에서 얻은 인가 승인 정보를 이 엔드포인트에 보내고 액세스 토큰을 발급받는다. 보통 이 과정에서 클라이언트 인증까지 함께 수행된다. 

모든 인가 승인 유형이 이 두 개의 엔드포인트를 모두 사용 X

확장 인가 승인 유형은 필요에 따라 추가적인 엔드포인트 정의 가능

Authorization endpoint - 리소스 소유자가 등장하고 브라우저 리다이렉션을 통해 허락할지 말지를 결정하는 곳
Token endpoint - 사용자는 등장하지 않고 클라이언트가 인가 서버와 직접 통신하여 토큰을 발급받는 곳

 

3.1 인가 엔드포인트(Authorization Endpoint)

리소스 소유자와 상호작용하여 인가 승인 정보(authorization grant)를 얻기 위해 사용된다. 인가 서버는 반드시 먼저 리소스 소유자의 신원을 확인해야함. 인가 서버가 리소스 소유자를 인증하는 방식은 이 문서에서 다루지 않는다.

 

클라이언트가 인가 엔드 포인트의 위치를 얻는 방법 역시 이 문서에서 다루지 않지만 보통은 각 서비스 문서가 알려줌

 

  • 엔드포인트 URI 구조 관련 규칙

인가 엔드포인트 URI에는 부록 B에 정의된대로  "application/x-www-form-urlencoded" 형식으로 이미 쿼리 파라미터(RFC3986)가 붙을 수도 있다. 이 경우 클라이언트가 OAuth 요청 파라미터(response_type, client_id 등)를 추가할 때 기존에 있던 쿼리 파라미터 지우면 안 된다.  그리고 중요한 것은 URL 끝에 #fragment 부분은 절대 포함하면 안된다.

fragment 이유 - fragment는 서버로 전송되지 않고 브라우저 내부에서만 처리되기 때문에 OAuth 요청 처리에 쓰일 수 없음
쿼리는 있어도 되지만(fragment는 안 되고), 기존 쿼리는 반드시 유지!!!

 

 

인가 엔드 포인트의 요청은 사용자가 로그인함, 사용자 ID, 비밀번호 같은 민간함 정보가 오감 이 정보들이 HTTP 메시지 안에서 암호화되지 않은 형태로 처리될 수 있음 그래서 인가 서버는 반드시 TLS(HTTPS)로만 인가 요청을 받아야함.

 

인가 서버는 인가 엔드 포인트에 대해 GET 방식(RFC2616)으로 보내는 것이 표준이다. 브라우저 리다이렉션으로 호출되기 떄문 그래서 인가 서버는 GET 요청을 반드시 지원 하지만 POST 요청도 지원해도 됨

 

요청 파라미터를 어떻게 해석해야하는 지에 대한 규칙이다.

1. 값이 없는 파라미터 경우 없는 것처럼 처리 진행하고

response_type=

2. OAuth가 정의하지 않는 파라미터는 그냥 무시(에러 내면 안됨)

foo=bar

3. 같은 파라미터를 여러 번 보내는 것은 허용 되지 않는다. 요청/응답 모두에서 각 파라미터는 한 번만 

response_type=code&response_type=token

 

3.1.1 응답 유형(Response Type)

인가 엔드포인트 -> 인가 코드 승인 방식(authorization code grant)암묵적 승인 방식(implicit grant) 흐름에서 사용 

클라이언트는 다음 파라미터를 사용해서 원하는 승인 유형(grant type)을 인가 서버에 알린다.

 

  • response_type
    • 이 파라밑터는 반드시 포함해야하는 파라미터(이 값이 없으면 인가 서버는 어떤 승인 방식을 원하는지 알 수 없음)
    • 허용되는 값은 다음과 같다. code(인가 코드 방식, 인가 서버가 인가 코드를 발급함, 이후 토큰 엔드포인트에서 액세스 토큰을 교환)[섹션 4.1.1], token(암묵적 승인 방식, 인가 서버가 액세스 토큰을 바로 발급)[섹션 4.2.1], 확장 값(extension response types)[섹션 8.4]는 OAuth 표준을 확장해서 새로 정의된 응답 유형 단 반드시 사전에 등록되고 명세로 정의된 값이어야함.
response_type = "인가 서버가 어떤 결과를 바로 돌려줄지에 대한 선언"

 

확장 응답 유형은 표준 값(code, token)말고 확장된 response_type을 사용하는 경우에는 하나의 값이 아니라 다음과 같이 처럼 여러 값을 동시에 나열할 수도 있다.

response_type=a b

이때 값들은 공백으로 구분(%x20), 순서는 의미 없음("a b" == "b a") 이 조합의 의미는 OAuth 기본 문서가 아니라 각 확장 명세에서 따로 정의

 

response_type이 아예 없는 경우, 값이 있지만 foo, abc123처럼 인가 서버가 이해하지 못하는 값인 경우 이때 인가 서버는 반드시 오류 응답을 반환

 

3.1.2 리다이렉션 엔드포인트(Redirection Endpoint)

인가 서버는 먼저 리소스 소유자와 상호작용한다.(로그인 및 권한 동의 등) 이 과정이 끝나면 인가 서버는 더 이상 사용자를 붙잡고 있지 않고 사용자의 브라우저(사용자 에이전트)를 다시 클라이언트 쪽으로 돌려보낸다. 이때 이동하는 목적지는 아무 URL이 아니라 미리 정해져 있고 인가 서버가 신뢰하는 클라이언트의 리다이렉션 엔드 포인트 그 다이렉션 엔드 포인트는 클라이언트가 사전에 등록했거나 인가 요청을 보낼 때 인가 서버와 합의된 주소

인가 서버는 사용자 인증이 끝나면 브라우저를 신뢰된 클라이언트 주소로만 되돌려 보낸다라는 의미

 

리다이렉션 엔드포인트는 https://example.com/callback[RFC3986] 4.3절에 정의된 대로 다음과 같은 완전한 주소(absolute URI)여야한다. 이유는 인가 서버가 정확히 어디로 보내야 하는지 명확히 알 수 있어야 하고 공격자가 주소를 교모하게 바꾸는 걸 방지하기 위해서이다. 리다이렉션 URI에는 부록 B에 정의된대로  "application/x-www-form-urlencoded" 형식으로 이미 쿼리 파라미터일 수 있다. OAuth 과정에서 인가 코드, state 값 같은 파라미터를 추가로 붙일 수 있다. 하지만 원래 URI에 있던 쿼리 파라미터를 지워서는 안된다. 엔드포인트 URI는 프래그먼트 구성 요소 포함하면 안된다. 즉, #뒤 fragment를 포함하면 안된다. fragment는 브라우저 내부에서만 처리되고 서버로 전달되지 않기 때문에 인가 서버가 정확히 제어하거나 검증하지 못함

 

3.1.2.1 엔드포인트 요청 기밀성(Endpoint Request Confidentiality) - 추가

리다이렉션 엔드포인트로 전달되는 값에는 code(인가 코드)  또는 token(액세스 토큰)처럼 민감한 자격 증명이 포함될 수 있다. 이 경우(색션 1.6)에 설명된대로 TLS(HTTPS)를 사용하는 것이 바람직하다. 이 명세는 TLS 사용을 의무화하지 않는다. 그 이유는 이 문서가 작성된 시점에서 클라이언트에게 TLS 배포를 요구하는 것이 많은 클라이언트 개발자들에게 상당한 장애물이 되었기 때문 TLS를 사용할 수 없는 경우 인가 서버는 사용자에게 경고("이 연결은 안전하지 않습니다")하는 것이 바람직

 

3.1.2.2 등록 요구 사항(Registration Requirments) - 추가

인가 서버는 다음에 해당하는 클라이언트에 대해 리다이렉션 엔드포인트를 반드시 등록하도록 요구해야함

  • 공개 클라이언트(public clients)
  • 암묵적 승인 방식(implicit grant type)을 사용하는 기밀 클라이언트

인가 서버는 모든 클라이언트가 인가 엔드포인트를 사용하기 전에 리다이렉션 엔드포인트를 등록하도록 요구하는 것이 바람직

 

인가 서버는 클라이언트가 완전한 리다이렉션 URI 전체를 제공하도록 요구하는 것이 바람직(클라이언트는 요청별 맞춤 처리를 위해 URI를 바꾸는 대신 state 파라미터를 사용하면 된다.) 만약 완전한 리다이렉션 URI의 등록을 요구하는 것이 불가능한 경우 인가 서버는 URI스킴(scheme)[ex - https], 권한(authority)[ex - example.com], 경로(path)[ex - /callback]까지 등록을 요구하는 것이 바람직(이 경우 클라이언트는 인가 요청시 리다이렉션 URI의 쿼리 구성 요소만을 변경할 수 있다)

 

인가 서버는 클라이언트가 여러 개의 리다이렉션 엔드포인틀르 등록하는 것을 허용

 

리다이렉션 URI 등록 요구 사항이 없는 경우, 섹션 10.15에 설명된 바와 같이 공격자가 자신이 원하는 URL을 redirect_uri로 넣을 수 있다. 그 결과 인가 서버가 인가 코드나 액세스 토큰을 공격자가 지정한 주소로 그대로 보내버릴 수 있다.

위 문단에서 Open Redirector 취약점, OAuth에서 매우 치명적인 보안 문제

 

3.1.2.3 동적 구성(Dynamic Configuration) - 추가

redirect_uri를 생략해도 되는 경우가 거의 없다는 것 다음과 같이 하나라도 해당되면

  • 클라이언트에 여러 개의 리다이렉션 URI가 등록되어 있거나
  • 리다이렉션 URI의 일부만 등록되어 있거나
  • 아예 리다이렉션 URI가 등록되어 있지 않은 경우

인가 서버는 "어디로 돌려보내야 할지"를 요청만 보고 판단할 수 없다 그래서 클라이언트가 인가 요청을 보낼 때 redirect_uri 요청 파라미터를 통해 실제로 사용할 리다이렉션 URI를 명시적으로 포함해야함

 

인가 요청에서(클라이언트가) 리다이렉션 URI(redirect_uri)가 포함된 경우 인가 서버는 등록된 리다이렉션 URI(또는 URI 구성 요소) 중 하나 이상과 일치하는지 확인해야함. 확인할 때 인가 서버는 [RFC3986] 섹션 6.2.1에 정의된 단순 문자열 비교(simple string comparsion) 방식으로 두 URI를 반드시 비교 만약 검증 안 하면 공격자가 인가 서버를 토큰 전달용 중계기(open redirector)로 악용할 수 있다. 

 

3.1.2.4  유효하지 않은 엔드포인트(Invalid Endpoint) - 추가

  • 인가 요청이 누락되었거나
  • 유효하지 않거나
  • 일치하지 않는 리다이렉션 URI로 인해 검증에 실패한 경우

위 경우 인가 서버는 리소스 소유자에게 해당 오류를 알려야한다. 유효하지 않은 리다이렉션 URI로 사용자 에이전트를 자동으로 리다이렉트해서는 안 된다.

 

3.1.2.5  엔드포인트 콘텐츠(Endpoint Content) - 추가

인가 서버가 사용자 에이전트(브라우저)를 클라이언트의 리다이렉션 엔드포인트로 보내면 그 요청의 결과로 보통 브라우저가 처리하는 HTML 응답 페이지가 반환된다. 문제는 여기서 발생한다. 만약 이 HTML 페이지가 리다이렉션 요청의 직접적인 결과물이라면 그 페이지 안에 포함된 모든 스크립트는 현재 URL 전체에 접근할 수 있고 그 URL에 포함된 인가 코드, 액세스 토큰 등 자격 증명도 그대로 읽을 수 있다. 

 

클라이언트는 리다이렉션 엔드포인트 응답에 제 3자 스크립트를 포함하지 않는 것 바람직 

제 3자 스크립트 - Google Analytics(외부 분석 도구), 소셜 로그인 플로그인, 광고 스크립트, 외부 CDN에서 불러오는 JS 등

OAuth가 권장하는 안전한 패턴은 다음과 같다.

  • 리다이렉션 엔드포인트에서 URL에 포함된 code,token의 자격증명을 가장 먼저 추출 
  • 그 자격 증명을 서버 세션, 안전한 내부 저장소 등으로 옮긴다.
  • 그런 다음 URL에서 자격 증명이 완전히 제거된 상태로 사용자 에이전트를 다른 내부 페이지로 다시 리다이렉트한다.

이렇게 하면 브라우저 주소창에도 토큰이 남지 않고 이후에 로드되는 페이지에서도 자격 증명에 접근할 수 없게된다. 그래서 클라이언트에서 URI로부터 자격 증명을 추출한뒤 이 자격 증명을 URI나 다른 위치에 노출하지 않고 사용자 에이전트를 다시 다른 엔드포인트로 리다이렉트 하라는 거(SHOULD) 만약 제 3자 스크립트가 포함되어 있다면 클라이언트가 작성한 스크립트가 가장 먼저 실행되어 URL 자격 증명을 즉시 추출 및 제거 하고 제 3자 스크립트가 실행되도록 해야함.

 

3.2 토큰 엔드포인트(Token Endpoint)

토큰 엔드포인트는 인가 결과를 실제 엑세스 토큰으로 바꾸는 곳이다. 클라이언트는 여기서 인가 코드 또는 리프레시 토큰을 제출하고 그 대가로 액세스 토큰을 받는다. 대부분 OAuth 흐름에서는 인가 -> 토큰 교환 이라는 단계가 필요하기 때문에 토큰 엔드포인트를 반드시 거친다. 단, 암묵적 승인 방식(implicit grant)를 제외한 경우(이 방식은 액세스 토큰이 직접 발급되기 때문)

 

클라이언트가 토큰 엔드포인트의 위치를 얻는 방법은 이 문서에서 벗어난다. 이건 서비스 문서에서 제공된다.

 

엔드포인트는 URI에는 부록 B에 정의된대로  "application/x-www-form-urlencoded" 형식으로 이미 쿼리 파라미터(RFC3986)가 붙을 수도 있다. 이 경우 클라이언트가 요청을 만들 때 기존에 있던 쿼리 파라미터 지우면 안 된다.  그리고 중요한 것은 URL 끝에 #fragment 부분은 절대 포함하면 안된다.

 

토큰 엔드포인트로의 요청에는 인가 코드, 리프레시 토큰, 클라이언트 비밀(client_secret), 액세스 토큰 자체 같은 치명적으로 민감한 정보가 포함된다. 이 정보들은 평문 자격 증명의 전송을 발생시키므로 인가 서버는 섹션 1.6에 설명된대로 TLS 사용

 

클라이언트는 액세스 토큰 요청을 수행할 때 HTTP "POST" 메서드만 사용해야한다.

이 요청만 허용하는 이유는 GET 요청은 URL에 파라미터가 노출되고, 로그, 히스토리, 캐시에 남기 쉽다. 토큰 요청은 이런 노출을 최대한 피해야한다. 그래서 토큰 요청은 반드시 POST로 보내고 요청 본문(body)에 포함되도록 한다.

 

값이 없는 상태로 전송된 파라미터는 존재하지 않는 것과 동일하게 취급, 인가 서버는 인식할 수 없는 요청 파리미터는 반드시 무시, 같은 파라미터가 한 번을 초과하여 포함되어서는 안된다.

 

3.2.1 클라이언트 인증(Redirection Authentication)

기밀 클라이언트나 클라이언트 자격 증명(client_id + client_secret 등)이 발급된 클라이언트는 토큰 엔드포인트로 요청을 보낼 떄 2.3 절에 설명된 방식으로 반드시 인가 서버에 증명해야한다. 클라이언트 인증은 다음과 같은 목적을 위해 사용된다.

 

  • 인가 코드와 리프레시 토큰은 특정 클라이언트를 위해 발급된 자격 증명이다. 클라이언트 인증을 하며 인가 서버는 "이 토큰을 지금 사용하는 클라이언트가 정말 이 토큰을 발급받았던 그 클라이언트인가"를 확인할 수 있다. 이 검즈은 인가 코드가 HTTPS가 아닌 환경 등 안전하지 않은 채널을 통해 전달되었거나, 리다이렉션 URI가 완전히 엄격하게 등록되지 않은 경우에 매우 중요한 역할을 한다.
  • 만약 어떤 클라이언트가 해킹되거나 내부 정보가 유출되었다면 공격자라 리프레시 토큰을 가지고 있을 수도 있다. 이때 인가서버는 해당 클라이언트를 비활성화하거나, client_secret 같은 자격 증명을 변경함으로써 공격자의 모든 토큰 사용을 즉시 차단할 수 있다. 이 방식은 이미 발급된 모든 리프레시 토큰을 하나하나 폐기하는 것보다 훨씬 빠르고 관리가 쉽다. 
  • 보안 관점에서 비밀 값(client_secret 등)은 주기적으로 교체하는 것이 권장된다. 만약 인증이 리프레시 토큰에만 의존한다면 모든 토큰을 다시 발급해야 해서 관리가 어렵다. 하지만 클라이언트 인증을 사용하면 클라이언트 자격 증명 하나만 교체해도 전체 보안 상태를 갱신할 수 있다.

 

클라이언트는 토큰 엔드포인트로 요청을 보낼 때 client_id를 포함해서 "이 요청을 보낸 주체가 누구인지"를 밝힐 수 있다. 하지만 이 자체는 인증이 아니라 식별에 가깝다. 인증되지 않은 클라이언트는 client_secret 등으로 자신을 증명할 수 없기 떄문에 최소한 이 인가 코드는 이 client_id를 위해 발급된 것이다를 인가 서버가 확인할 수 있어야한다. 그래서 "authorization_code", "grant_type" 에서는 인증을 하지 않는 경우라도 client_id를 반드시 보내도록 강제한다. 이는 인증 코드 치환(substitution)을 방지하기 위한 것이다.

인증 코드 치환 공격이란 - 다른 클라이언트를 위해 발급된 인가 코드를 실수나 공격으로 받아서 사용하는 상황을 말한다. 
client_id 확인은 이런 코드 혼동 및 치환 문제를 막기 위한 장치이다. 다만 이 조치는 리소스 서버 접근 자체를 더 안전하게 만드는 것은 아니고 토큰 교환 단계의 정확성을 보장하기 위한 보호에 가깝다.

 

3.3 액세스 토큰 범위(Access Token Scope)

클라이언트는 인가 엔드포인트나 토큰 엔드포인트를 요청을 보낼 때 "scope" 요청 파라미터를 사용하여 "어디까지 접근하고 싶은지"를 명시할 수 있다. 이에 인가 서버는 실제로 발급한 액세스 토큰이 어떤 범위의 권한을 가지는 지를 "scope" 응답 파라미터로 다시 클라이언트에게 알려준다. 

 

scope 값은 하나 이상의 문자열로 이루어질 수 있고 공백(space)으로 구분된다. 각 문자열은 대소문자(case-sensitive)를 구분하며 인가 서버가 의미를 정의한다. 또한, 값이 여러 개의 공백으로 구분된 문자열을 포함하는 경우, 문자열들의 순서는 중요하지 않으며 각 문자열은 요청된 독립적인 권함 범위를 추가한다.

scope=read write

는 read 권한 + write 권한을 함께 요청한다는 뜻

 

scope       = scope-token *( SP scope-token )
scope-token = 1*( %x21 / %x23-5B / %x5D-7E )
(위는 scope 값이 어떤 문자들로 구성될 수 있는지를 정의한 문법)

- 추가작성
이 부분은 scope 값의 기술적인 문법 규칙을 정의한 것 
요지는 scope는 하나 이상의 scope-token으로 이루어지고 각 token은 공백으로 구분된다. scope-token은
특정 ASCII 문자 범위만 허용된 문자열이다.

 

클라이언트가 어떤 scope를 요청하더라도 인가 서버는 자신의 정책이나 리소스 소유자의 설정에 따라 그 요청을 전부 또는 일부 거절할 수 있다. 이때 요청한 scope와 실제로 발급된 토큰의 scope가 다르면 인가서버는 반드시 "scope" 응답 파라미터로 실제로 허용된 범위를 클라이언트에게 명시해야한다.

 

클라이언트가 scope를 보내지 않았다면 인가 서버는 아무 처리도 안하고 넘어가면 안되고 둘 중 하나를 선택해야함

  • 미리 정의된 기본 scope로 처리
  • scope가 잘못되었다고 판단하고 요청을 실패

인가 서버는 어던 scope를 요구하는지 기본 scope가 있다면 그것이 무엇인지 문서로 명확히 설명해 두는 것이 바람직