사전 OAuth 2.0
표준

OAuth 2.0

gabury1

남의 서비스에 있는 내 데이터를 다른 앱이 대신 쓰게 해 주는 규격입니다. 사진 공유 서비스에 올려 둔 사진을 인쇄 서비스가 가져가 인화하는 일이 그렇습니다. 비밀번호를 그 앱에 넘기는 대신 권한이 담긴 문자열 하나를 발급받아 건넵니다. 그 문자열을 접근 토큰이라고 부릅니다.

쉽고 빠른 이해

OAuth 2.0 은 비밀번호를 다른 앱에 안 주고도 그 앱이 내 데이터에 접근하게 해 주는 규격입니다. 사진 공유 서비스에 비밀번호를 인쇄 서비스에 그대로 주는 대신, 접근 토큰 하나만 건네서 인쇄 서비스가 내 사진만 가져가게 합니다.

비밀번호를 통째로 넘기면 그 앱이 계정 전체 권한을 갖고, 나중에 그 앱만 골라 접근을 끊을 수도 없습니다. 접근 토큰은 쓸 수 있는 범위와 기간을 정해서 발급하고, 앱마다 따로 거둬들일 수 있습니다.

돌아가는 순서는 이렇습니다.

  1. 앱이 사용자를 승인 화면으로 보내 허락을 구합니다
  2. 사용자가 허락하면 접근 토큰이 발급됩니다
  3. 앱이 그 토큰을 들고 데이터를 가진 서버에 요청합니다

대가는 절차가 늘어난다는 것입니다. 앱 종류마다 맞는 발급 방식을 따로 골라야 하고, 방식을 잘못 고르면 토큰이 새거나 가로채일 위험이 커집니다. 어떤 앱에 무엇이 맞는지는 아래 「갈래」의 첫 표가 가릅니다.

상세

OAuth 2.0 은 제3자 애플리케이션이 HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜) 서비스의 자원 일부에만 손대게 해 주는 인가 프레임워크입니다. 쓰임이 둘입니다. 자원 소유자를 대신해 쓰는 경우, 그리고 애플리케이션이 자기 몫으로 쓰는 경우입니다. 앞의 경우에는 인가 서버가 자원 소유자와 HTTP 서비스 사이에 끼어 허락을 받아 냅니다.

오가는 쪽마다 이름이 따로 붙습니다. 이 이름들이 문서 끝까지 그대로 쓰입니다.

이름 무엇인가
자원 소유자 보호된 자원에 대한 접근을 승인할 수 있는 주체입니다. 사람이면 최종 사용자라고 부릅니다
자원 서버 보호된 자원을 보관하는 서버입니다. 접근 토큰을 실어 오는 요청을 받아들이고 응답합니다
클라이언트 자원 소유자를 대신해, 그의 인가를 받아 보호 자원을 요청하는 애플리케이션입니다. 서버에서 돌든 데스크톱에서 돌든 다른 기기에서 돌든 구현을 한정하지는 않습니다
인가 서버 자원 소유자를 인증하고 인가를 얻은 뒤 클라이언트에 접근 토큰을 발급하는 서버입니다
기밀 클라이언트 인가 서버에 등록해 둔 자격증명으로 스스로 인증하는 클라이언트
공개 클라이언트 그 인증 없이 도는 클라이언트. 브라우저 안에서 도는 JavaScript 가 그렇습니다
사용자 에이전트 자원 소유자가 쓰는 프로그램. 보통 웹 브라우저입니다
리다이렉션 엔드포인트 인가 서버가 사용자 에이전트를 되돌려 보내는 클라이언트 쪽 진입점. 그 주소가 리다이렉션 URI(Uniform Resource Identifier, 통합 자원 식별자) 입니다

사진으로 옮기면 최종 사용자가 자원 소유자, 인쇄 서비스가 클라이언트, 사진 공유 서비스가 자원 서버입니다. 인가 서버와 자원 서버를 어떻게 잇는지는 명세가 정하지 않았습니다. 둘이 같은 서버여도 되고, 인가 서버 하나가 여러 자원 서버에서 통하는 토큰을 발급해도 됩니다.

OAuth 이전에는 제3자 애플리케이션에 자원을 열어 주려면 자원 소유자가 자기 자격증명을 그 애플리케이션과 공유해야 했습니다. 자격증명을 통째로 넘기면 저장 · 취소 · 범위 · 연쇄 유출에서 문제가 갈라져 나옵니다.

문제
제3자 애플리케이션이 자원 소유자의 자격증명을 나중에 쓰려고 저장해야 합니다. 보통은 평문 비밀번호입니다
비밀번호가 원래 지고 있는 보안 약점을 알면서도 서버가 비밀번호 인증을 지원해야 합니다
제3자 애플리케이션이 지나치게 넓은 접근을 얻습니다. 자원 소유자에게는 기간을 제한하거나 자원의 일부만 열어 줄 방법이 없습니다
자원 소유자가 제3자 하나만 골라 접근을 취소할 수 없습니다. 전부 취소해야 하고, 그러려면 비밀번호를 바꿔야 합니다
제3자 애플리케이션 하나가 뚫리면 최종 사용자의 비밀번호와 그 비밀번호로 보호되던 모든 데이터가 함께 뚫립니다

OAuth 는 인가 레이어를 넣고 클라이언트의 역할을 자원 소유자의 역할과 분리해 이 문제들을 다룹니다. 인증이 「누구인지 확인하는 것」이라면 인가는 「그 확인된 상대에게 무엇을 허락할지 정하는 것」입니다. 클라이언트는 자원 소유자의 것과는 다른 자격증명 한 벌을 발급받습니다. 그것이 접근 토큰이고, 쓸 수 있는 범위와 수명과 그 밖의 접근 속성을 나타내는 문자열입니다.

이 프레임워크는 HTTP 와 함께 쓰도록 설계됐고, HTTP 가 아닌 프로토콜 위에서 쓰는 방법은 정하지 않았습니다. OAuth 1.0 과는 하위 호환되지 않아, 새 구현은 OAuth 2.0 을 쓰고 OAuth 1.0 은 기존 배치를 떠받치는 데만 씁니다.

여섯 단계

네 역할이 이런 차례로 오갑니다. 구체적인 왕복은 아래 「갈래」의 그랜트마다 다릅니다.

단계 무슨 일이 일어나나
A 클라이언트가 자원 소유자에게 인가를 요청합니다. 직접 할 수도 있고 인가 서버를 중개자로 두고 간접적으로 할 수도 있는데, 명세는 간접 방식을 선호합니다
B 클라이언트가 인가 그랜트를 받습니다. 인가 그랜트는 자원 소유자의 인가를 나타내는 자격증명이고, 아래 「갈래」의 네 유형 중 하나나 확장 유형으로 표현됩니다
C · D 클라이언트가 인가 서버에 인증하면서 인가 그랜트를 제시하고 접근 토큰을 요청합니다. 인가 서버는 클라이언트를 인증하고 인가 그랜트를 검증해, 유효하면 접근 토큰을 발급합니다
E · F 클라이언트가 자원 서버에 보호된 자원을 요청하면서 접근 토큰을 제시해 인증합니다. 자원 서버는 토큰을 검증하고, 유효하면 요청에 응합니다

접근 토큰과 리프레시 토큰

토큰은 자원을 부르는 데 쓰는 것과 그 토큰을 새로 받아 오는 데 쓰는 것으로 갈립니다.

접근 토큰은 보호된 자원에 접근하는 데 쓰는 자격증명입니다. 자원 소유자가 승인하고 자원 서버와 인가 서버가 강제하는 범위와 접근 기간을 나타내는 문자열입니다. 클라이언트는 대개 그 안을 들여다볼 수 없고 값이 어떻게 생겼는지 알 필요도 없습니다. 이것을 불투명하다고 합니다. 값 자체가 인가 정보를 가져올 식별자일 수도 있고, 서명된 데이터로 인가 정보를 자기 안에 담고 있을 수도 있습니다.

접근 토큰은 추상화 레이어이기도 합니다. 아이디와 비밀번호처럼 서로 다른 인가 수단을 자원 서버가 전부 이해할 필요 없이, 자원 서버가 아는 토큰 하나로 갈음합니다. 토큰의 속성과 보호 자원에 접근하는 방법은 이 문서가 아니라 동반 명세가 정의합니다. 토큰과 값의 크기도 정해 두지 않았습니다. 클라이언트는 값 크기를 가정하지 않는 편이 낫고, 인가 서버는 자신이 발급하는 값의 크기를 문서화하는 것이 좋습니다(SHOULD).

리프레시 토큰은 접근 토큰을 얻는 데 쓰는 자격증명입니다. 지금 접근 토큰이 무효가 되거나 만료됐을 때 새 것을 받아 오고, 같거나 더 좁은 범위의 토큰을 추가로 얻는 데도 씁니다. 발급할지 말지는 인가 서버의 재량이고, 발급한다면 접근 토큰과 함께 실어 보냅니다. 인가 서버하고만 쓰도록 의도됐고 자원 서버에는 절대 보내지 않습니다.

두 엔드포인트

서버 쪽 진입점은 사람을 상대하는 쪽과 기계를 상대하는 쪽으로 갈립니다.

인가 엔드포인트는 자원 소유자와 상호작용해 인가 그랜트를 얻는 쪽입니다. 인가 서버는 먼저 자원 소유자의 신원을 확인합니다. 아이디와 비밀번호 로그인이든 세션 쿠키든 어떻게 인증할지는 명세가 정하지 않았습니다. 인가 서버는 여기서 HTTP GET 메서드를 지원해야 하고 POST 도 지원할 수 있습니다.

토큰 엔드포인트는 클라이언트가 인가 그랜트나 리프레시 토큰을 제시해 접근 토큰을 얻는 쪽입니다. 임플리싯 그랜트를 뺀 모든 그랜트에서 쓰입니다. 임플리싯 그랜트는 인가 요청 하나로 접근 토큰을 바로 받는 방식이라 이 왕복이 없습니다. 클라이언트는 접근 토큰 요청에 HTTP POST 메서드를 써야 합니다.

두 엔드포인트에 공통으로 걸리는 규칙은 전부 강제입니다. 쿼리 컴포넌트는 URI 에서 ? 뒤에 붙는 부분, 프래그먼트 컴포넌트는 # 뒤에 붙는 부분입니다.

규칙 강도
엔드포인트 URI 에 쿼리 컴포넌트가 들어 있으면 파라미터를 덧붙일 때 그 쿼리를 유지합니다 MUST
엔드포인트 URI 에 프래그먼트 컴포넌트를 넣지 않습니다 MUST NOT
값 없이 보낸 파라미터는 요청에서 빠진 것으로 취급합니다 MUST
인가 서버는 알아보지 못하는 요청 파라미터를 무시합니다 MUST
같은 요청·응답 파라미터를 두 번 넣지 않습니다 MUST NOT

명세가 일부러 안 정한 것도 많습니다. 클라이언트 등록 · 인가 서버의 능력 · 엔드포인트 발견은 미정의로 남겼고, 클라이언트가 엔드포인트 위치를 어떻게 알아내는지도 정하지 않았습니다. 고를 수 있는 부품이 많고 확장 여지가 커서 서로 안 맞물리는 구현이 잔뜩 나올 수 있다고 명세가 스스로 적어 두었습니다. 그래서 붙일 서버가 정해지면 그 서버의 문서를 먼저 봐야 합니다. 손으로 일일이 맞춰 줘야 물립니다.

갈래

갈리는 축은 클라이언트가 무엇을 제시해서 접근 토큰과 바꾸느냐 — 인가 그랜트입니다. 위 「쉽고 빠른 이해」가 「발급 방식」이라고 부른 것이 이 인가 그랜트입니다. 표준 본문이 네 유형을 정의했고, 유형을 더 얹을 확장 메커니즘도 열어 두었습니다. 맨 뒤에는 다음 판인 OAuth 2.1 을 붙였습니다. 그 판이 이 그랜트들 가운데 둘을 아예 뺐기 때문입니다.

무엇을 고를지는 자기 자격증명을 숨길 수 있느냐와 브라우저를 띄울 수 있느냐로 갈립니다. 표에 나오는 PKCE(Proof Key for Code Exchange, 코드 교환 증명 키)는 인가 코드 그랜트에 얹는 보호 장치이고, 바로 아래에서 다룹니다.

어떤 앱이면 무엇을 쓰나 지금 상태
서버에서 도는 웹 앱처럼 자격증명을 숨길 수 있는 기밀 클라이언트 인가 코드 그랜트 OAuth 2.1 은 여기에도 PKCE 파라미터를 요구합니다
브라우저 안 JavaScript 처럼 자격증명을 숨길 수 없는 공개 클라이언트 인가 코드 그랜트에 PKCE 를 얹습니다 임플리싯 그랜트는 쓰지 않는 것이 좋습니다(SHOULD NOT)
스마트 TV · 미디어 콘솔 · 프린터처럼 입력과 브라우저가 없는 기기 디바이스 인가 그랜트 표준 본문 밖의 확장 그랜트입니다
사람이 끼지 않고 자기 통제 아래의 자원만 부르는 서버 클라이언트 자격증명 그랜트 기밀 클라이언트만 써야 합니다(MUST)
아이디와 비밀번호를 앱이 직접 받는 방식 쓰지 않습니다 금지입니다(MUST NOT)

인가 코드 그랜트

접근 토큰과 리프레시 토큰을 둘 다 얻는 데 쓰고, 기밀 클라이언트에 맞춰져 있습니다. 리다이렉션 기반 흐름이라 클라이언트가 사용자 에이전트와 상호작용할 수 있어야 하고 인가 서버에서 리다이렉션으로 들어오는 요청도 받아야 합니다. 인가 요청과 토큰 요청이 따로 나가는 것이 특징이고, 실물 왕복은 아래 「예시」에 있습니다.

PKCE

PKCE 는 그랜트를 새로 만드는 것이 아니라 인가 코드 그랜트에 얹는 확장입니다. 공개 클라이언트가 인가 코드 가로채기 공격에 취약하기 때문에 나왔습니다.

공격자는 TLS(Transport Layer Security, 전송 계층 보안)로 보호되지 않는 통신 경로에서 인가 엔드포인트가 돌려준 인가 코드를 가로챕니다. 클라이언트 운영체제 안의 애플리케이션 간 통신이 그런 경로입니다. 인가 코드를 쥐면 그것으로 접근 토큰을 얻을 수 있습니다.

막는 방법은 코드를 발급받은 쪽만 아는 비밀을 하나 걸어 두는 것입니다. 클라이언트는 인가 요청마다 code_verifier 를 만듭니다. A-Z a-z 0-9 - . _ ~ 만 쓰고 추측하기 어렵도록 고르게 흩어 뽑은 난수 문자열이며, 길이는 최소 43자 최대 128자입니다.

여기서 code_challenge 를 파생시키는 변환이 둘입니다. plain 은 code_challenge = code_verifier 이고, S256 은 code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) 입니다. 서버 쪽에서 S256 이 MTI(Mandatory To Implement, 구현 필수)라, 클라이언트가 쓸 수 있으면 반드시 S256 을 써야 합니다(MUST). plain 은 기술적 이유로 S256 을 못 쓰고, 서버가 plain 을 받는다는 것을 이 요청·응답 밖의 경로로 미리 알고 있을 때만 허용됩니다. 클라이언트는 인가 요청에 code_challenge 를 REQUIRED, code_challenge_method 를 OPTIONAL 파라미터로 실어 보내는데, 뒤엣것을 빠뜨리면 plain 으로 잡힙니다.

임플리싯 그랜트

접근 토큰만 얻고 리프레시 토큰 발급을 지원하지 않습니다. 특정 리다이렉션 URI 를 운영하는 것으로 알려진 공개 클라이언트에 맞춰져 있고, 그런 클라이언트는 보통 브라우저 안에서 JavaScript 같은 스크립팅 언어로 구현됩니다.

인가 코드 그랜트와 달리 인가 요청 하나의 결과로 접근 토큰을 바로 받습니다. 클라이언트 인증이 없고, 자원 소유자가 그때 함께 있다는 것과 리다이렉션 URI 가 등록돼 있다는 것에 기댑니다. 접근 토큰이 리다이렉션 URI 에 인코딩되므로 같은 기기의 다른 애플리케이션에 노출될 수 있습니다.

임플리싯 그랜트는 쓰지 않는 것이 좋습니다(SHOULD NOT). 인가 응답에서 접근 토큰을 발급하는 방식은 접근 토큰 유출과 접근 토큰 재생에 취약하기 때문입니다. 접근 토큰을 발급받은 그 클라이언트만 쓸 수 있게 묶어 두는 것을 송신자 제한이라고 하는데, 인가 응답으로 나가는 토큰에는 이 방법이 표준화되어 있지 않습니다. 그래서 공격자가 유출되거나 탈취된 토큰을 자원 엔드포인트에서 그대로 쓸 수 있습니다. 대신 code 응답 타입을 쓰는 것이 좋습니다(SHOULD).

자원 소유자 비밀번호 자격증명 그랜트

자원 소유자가 클라이언트와 신뢰 관계에 있는 경우에 맞는 그랜트입니다. 기기 운영체제나 권한이 높은 애플리케이션이 그렇습니다. 아이디와 비밀번호를 대화형 폼으로 얻을 수 있는 클라이언트에 맞고, HTTP Basic 이나 Digest 인증을 쓰던 기존 클라이언트를 OAuth 로 옮기는 데도 씁니다.

자원 소유자 비밀번호 자격증명 그랜트는 쓰면 안 됩니다(MUST NOT). 임플리싯 그랜트가 SHOULD NOT 인 것과 강도가 다릅니다. 자원 소유자의 자격증명을 클라이언트에 안전하지 않게 노출하기 때문입니다.

클라이언트가 선의라 해도 자격증명이 인가 서버 말고도 여러 곳에서 샐 수 있게 되어 공격면이 넓어지고, 인가 서버가 아닌 곳에 자격증명을 입력하도록 사용자를 길들입니다. 또 2단계 인증처럼 여러 번의 사용자 상호작용이 필요한 절차를 애초에 염두에 두지 않았습니다.

클라이언트 자격증명 그랜트

클라이언트가 자기 통제 아래 있는 보호된 자원에 접근할 때, 또는 인가 서버와 미리 합의된 다른 자원 소유자의 자원에 접근할 때 자기 클라이언트 자격증명만으로 접근 토큰을 요청합니다. 미리 합의하는 방법까지는 정하지 않았습니다. 클라이언트 자격증명 그랜트는 기밀 클라이언트만 써야 합니다(MUST).

확장 그랜트

토큰 엔드포인트의 grant_type 값으로 인가 서버가 정의한 절대 URI 를 지정하고 필요한 파라미터를 덧붙여 씁니다. 명세가 드는 예는 SAML(Security Assertion Markup Language, 보안 주장 마크업 언어) 2.0 어서션 그랜트입니다. grant_type 이 urn:ietf:params:oauth:grant-type:saml2-bearer 이고 assertion 파라미터에 어서션이 실립니다. 응답은 다른 그랜트와 같습니다.

디바이스 인가 그랜트

표준 본문 밖에서 따로 정의된 확장 그랜트입니다. 입력 수단이 제한되어 있거나 적당한 브라우저가 없는 기기가 사용자 인가를 요청할 수 있게 합니다. 스마트 TV · 미디어 콘솔 · 디지털 액자 · 프린터 같은 것들입니다. 사용자에게는 스마트폰처럼 입력과 브라우저를 갖춘 보조 기기에서 인가 요청을 검토하라고 안내합니다. 디바이스 플로우라고도 부릅니다.

쓰려면 기기가 이미 인터넷에 연결돼 있어야 하고, 바깥으로 HTTPS(HyperText Transfer Protocol Secure, 보안 하이퍼텍스트 전송 프로토콜) 요청을 보낼 수 있어야 하고, URI 와 코드 문자열을 사용자에게 표시하거나 달리 전달할 수 있어야 합니다. 사용자 쪽에는 그 요청을 처리할 보조 기기가 있어야 합니다.

기기 위의 클라이언트와 사용자 에이전트 사이에 양방향 통신이 필요 없다는 점이 앞의 두 그랜트와 다릅니다. 클라이언트가 들어오는 요청을 받을 수 없으므로, 최종 사용자가 승인을 마칠 때까지 인가 서버를 반복해서 폴링합니다. 스마트폰처럼 능력 있는 기기의 네이티브 앱은 이쪽이 아니라 외부 브라우저를 띄우는 쪽입니다.

OAuth 2.1 초안

다음 판이 초안으로 진행 중이고, 2026년 8월 시점의 최신은 15판입니다. 지금 정본과 그 동반 문서의 Bearer 토큰 사용법을 둘 다 대체하고 폐기합니다. 뒤 문서들이 정본을 갱신하거나 폐기한 대목은 그 규범적 변경을 반영하거나 통째로 뺐습니다.

무엇이 달라지나
인가 코드 그랜트가 PKCE 기능으로 확장돼서, 기본 사용법이 PKCE 파라미터 추가를 요구합니다
리다이렉트 URI(2.0 이 리다이렉션 URI 라고 부르던 그 값)는 정확한 문자열 일치로 비교해야 합니다
임플리싯 그랜트, 곧 response_type=token 이 빠졌습니다
자원 소유자 비밀번호 자격증명 그랜트가 빠졌습니다
Bearer 토큰 사용법에서 URI 쿼리 문자열에 토큰을 싣는 방식이 빠졌습니다
공개 클라이언트의 리프레시 토큰은 송신자 제한이 걸려 있거나 일회용이어야 합니다
인가 코드를 담은 토큰 엔드포인트 요청에 redirect_uri 파라미터가 더 이상 들어가지 않습니다
인가 서버는 요청 본문에 실린 클라이언트 자격증명을 지원해야 합니다

임플리싯 그랜트를 뺀 것은 인가 응답에서 접근 토큰을 더 이상 발급하지 않겠다는 뜻이고, 그 동작을 가리키던 response_type=token 값도 정의되지 않습니다. 인가 엔드포인트에서 다른 산출물을 돌려주는 확장 응답 타입, 예컨대 OpenID Connect 가 정의한 response_type=id_token 은 영향을 받지 않습니다.

예시

인가 코드 그랜트 한 벌을 명세의 예제 그대로 따라갑니다. 줄바꿈은 보기 편하라고 넣은 것이고, 요청은 TLS 를 써서 보냅니다.

인가 요청

클라이언트가 사용자 에이전트를 이 URI 로 보내는 것이 출발입니다.

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

response_type 은 REQUIRED 이고 값이 code 여야 합니다(MUST). client_id 도 REQUIRED 이고, redirect_uri 와 scope 는 OPTIONAL 입니다. scope 는 접근 토큰이 미치는 범위로, 앞에서 「범위」라고 부른 것이 이 파라미터입니다. state 는 RECOMMENDED 이고 요청과 콜백 사이에서 상태를 유지하는 값인데, 인가 서버가 사용자 에이전트를 되돌릴 때 그대로 실어 주므로 CSRF(Cross-Site Request Forgery, 사이트 간 요청 위조)를 막는 데 쓰는 것이 좋습니다(SHOULD).

인가 서버는 필수 파라미터가 전부 있고 유효한지 확인하고, 자원 소유자를 인증해 인가 결정을 얻습니다. 승인이면 사용자 에이전트를 요청에 실려 온 redirect_uri 로 되돌리면서 인가 코드를 함께 실어 줍니다. 이 인가 코드가 위 「여섯 단계」 B 에서 말한 인가 그랜트의 실물입니다.

토큰 요청

받은 인가 코드를 접근 토큰과 바꾸는 왕복입니다.

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

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

grant_type 은 REQUIRED 이고 값이 authorization_code 여야 합니다(MUST). code 도 REQUIRED 이고, redirect_uri 는 인가 요청에 그 파라미터가 실렸다면 REQUIRED 입니다. 클라이언트 유형이 기밀이거나 클라이언트 자격증명을 발급받았다면 인가 서버에 인증해야 하는데(MUST), 위 예제의 Authorization: Basic 헤더가 그 인증입니다.

인가 서버가 이때 해야 하는 일이 다섯입니다(MUST).

해야 하는 일
기밀 클라이언트나 클라이언트 자격증명을 발급받은 클라이언트에는 클라이언트 인증을 요구합니다
클라이언트 인증이 실려 왔으면 클라이언트를 인증합니다
그 인가 코드가 인증된 기밀 클라이언트에 발급된 것인지 확인합니다. 공개 클라이언트라면 요청의 client_id 에 발급된 코드인지 확인합니다
인가 코드가 유효한지 검증합니다
최초 인가 요청에 redirect_uri 가 있었다면 이번 요청에도 있는지 확인합니다. 있다면 두 값이 같은지도 확인합니다

성공 응답

인가 서버가 접근 토큰을 돌려주는 응답입니다.

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"
}

응답 본문은 JSON(JavaScript Object Notation, 자바스크립트 객체 표기법)입니다. 파라미터는 구조의 최상위에 하나씩 붙고, 이름과 문자열 값은 JSON 문자열로 수치 값은 JSON 수로 적히며, 순서는 상관없습니다. 클라이언트는 알아보지 못하는 이름을 무시해야 합니다(MUST).

파라미터 요구 강도 무엇인가
access_token REQUIRED 인가 서버가 발급한 접근 토큰
token_type REQUIRED 발급된 토큰의 종류. 값은 대소문자를 안 가립니다
expires_in RECOMMENDED 접근 토큰의 수명, 초 단위. 값 3600 은 응답이 만들어진 시점부터 한 시간 뒤에 만료된다는 뜻입니다. 빠뜨렸다면 인가 서버는 다른 수단으로 만료 시각을 주거나 기본값을 문서화하는 것이 좋습니다(SHOULD)
refresh_token OPTIONAL 같은 인가 그랜트로 새 접근 토큰을 얻는 데 쓰는 리프레시 토큰
scope 클라이언트가 요청한 범위와 같으면 OPTIONAL, 다르면 REQUIRED 접근 토큰의 범위

예제의 "token_type":"example" 에서 example 은 명세가 예시용으로 채워 넣은 값이고, 실제로 쓰는 값이 아래 「보호된 자원 요청」의 Bearer 입니다.

오류 응답

요청이 잘못됐을 때 돌아오는 응답입니다. 따로 정하지 않은 한 상태 코드는 400 이고, error 는 REQUIRED 이며 값은 아래 여섯 ASCII(American Standard Code for Information Interchange, 미국 정보교환 표준부호) 오류 코드 중 하나입니다.

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

{
  "error":"invalid_request"
}
값 언제 오나
invalid_request 필수 파라미터가 빠졌거나, 지원하지 않는 파라미터 값이 들어갔거나, 파라미터를 되풀이했거나, 자격증명을 여러 벌 넣었거나, 클라이언트 인증에 두 가지 이상의 방법을 썼거나, 그 밖에 요청이 잘못된 꼴입니다
invalid_client 클라이언트 인증에 실패했습니다. 인가 서버는 어떤 HTTP 인증 스킴을 지원하는지 알리려고 401 을 돌려줄 수 있습니다(MAY). 클라이언트가 Authorization 요청 헤더로 인증을 시도했다면 401 로 응답하고 WWW-Authenticate 응답 헤더를 실어야 합니다(MUST)
invalid_grant 제시한 인가 그랜트나 리프레시 토큰이 유효하지 않거나, 만료됐거나, 폐기됐거나, 인가 요청에 쓴 리다이렉션 URI 와 맞지 않거나, 다른 클라이언트에 발급된 것입니다
unauthorized_client 인증된 클라이언트가 이 인가 그랜트 유형을 쓸 권한이 없습니다
unsupported_grant_type 인가 서버가 그 인가 그랜트 유형을 지원하지 않습니다
invalid_scope 요청한 범위가 유효하지 않거나, 알 수 없거나, 꼴이 잘못됐거나, 자원 소유자가 승인한 범위를 넘습니다

error_description 과 error_uri 는 OPTIONAL 입니다. 앞의 것은 무슨 오류가 났는지 클라이언트 개발자에게 알려 주는 ASCII 텍스트이고, 뒤의 것은 그 오류를 설명하는 웹 페이지의 URI 입니다.

보호된 자원 요청

여기서부터는 받은 토큰을 실제로 쓰는 단계입니다. 접근 토큰을 들고 자원을 부릅니다.

GET /resource HTTP/1.1
Host: server.example.com
Authorization: Bearer mF_9.B5f-4.1JqM

Authorization 요청 헤더로 접근 토큰을 보낼 때 클라이언트는 Bearer 인증 스킴을 씁니다. 자격증명의 문법은 credentials = "Bearer" 1*SP b64token 입니다. 1*SP 는 공백 하나 이상을 뜻하고, b64token 은 영문자와 숫자와 - . _ ~ + / 를 하나 이상 이어 붙인 뒤 = 를 붙일 수 있는 문자열입니다. 클라이언트는 이 헤더와 스킴을 쓰는 것이 좋고(SHOULD), 자원 서버는 이 방법을 지원해야 합니다(MUST).

출처 문서

정본은 RFC(Request for Comments) 6749 입니다. IETF(Internet Engineering Task Force, 국제 인터넷 표준화 기구)가 2012년 10월에 냈고, 헤더의 Category 는 Standards Track — 표준으로 굳기까지의 단계를 밟는 문서라는 뜻입니다. 편집자는 Microsoft 의 D. Hardt 입니다. 이 문서가 OAuth 1.0 을 기술한 RFC 5849 를 대체하고 폐기했으므로, OAuth 1.0 의 절 번호를 인용하면 폐기된 판을 드는 것입니다.

대체와 갱신

flowchart TD
    F["OAuth 2.1 초안<br>draft-ietf-oauth-v2-1"] -- 대체·폐기 예정 --> B["OAuth 2.0<br>RFC 6749"]
    F -- 대체·폐기 예정 --> C["RFC 6750<br>Bearer Token Usage"]
    E["RFC 9700<br>BCP 240 · 2025년 1월"] -- 갱신 --> B
    E -- 갱신 --> C
    E -- 갱신 --> D["RFC 6819<br>위협 모델과 보안 고려사항"]
    B -- 폐기 --> A["OAuth 1.0<br>RFC 5849"]

폐기와 갱신은 다릅니다. 폐기된 판은 더 이상 정본이 아니지만, 갱신된 판은 여전히 정본이고 갱신 문서가 그 위에 조언을 얹습니다.

확장 문서

문서 제목 · 상태 무엇을 더하나
RFC 6750 「The OAuth 2.0 Authorization Framework: Bearer Token Usage」 · Standards Track · 2012년 10월 Bearer 토큰을 HTTP 요청에 실어 보호된 자원에 접근하는 방법. 토큰을 가진 쪽은 누구든 암호 키의 소지를 증명하지 않고 그 자원에 닿으므로, 저장과 전송 양쪽에서 노출을 막아야 합니다
RFC 7009 「OAuth 2.0 Token Revocation」 · Standards Track · 2013년 8월 토큰 폐기 엔드포인트. 필요 없어진 토큰을 인가 서버에 알려 무효화하고, 해당되면 같은 인가 그랜트에 기반한 다른 토큰도 함께 무효화합니다
RFC 7662 「OAuth 2.0 Token Introspection」 · Standards Track · 2015년 10월 보호된 자원이 인가 서버에 질의해 토큰이 활성 상태인지와 그 토큰의 인가 문맥 정보를 얻는 방법
RFC 8414 「OAuth 2.0 Authorization Server Metadata」 · Standards Track · 2018년 6월 엔드포인트 위치와 인가 서버의 능력을 담은 메타데이터 형식
RFC 8252 「OAuth 2.0 for Native Apps」 · Best Current Practice · BCP 212 · Updates: 6749 · 2017년 10월 네이티브 앱에서 오는 인가 요청은 외부 사용자 에이전트, 주로 사용자의 브라우저를 통해서만 하는 것이 좋다고 정합니다
RFC 7636 Proof Key for Code Exchange 공개 클라이언트가 인가 코드 가로채기 공격을 막는 확장. code_verifier 와 code_challenge 를 정의합니다
RFC 8628 디바이스 인가 그랜트 입력 수단이 제한된 기기를 위한 그랜트 유형을 정의합니다
RFC 9700 「Best Current Practice for OAuth 2.0 Security」 · BCP 240 · Updates: 6749, 6750, 6819 · 2025년 1월 쌓인 실무 경험을 반영해 위협 모델과 보안 조언을 갱신하고, 덜 안전하다고 판단된 운용 방식 일부를 폐기합니다. 무엇을 어떤 강도로 폐기했는지는 위 「갈래」에 있습니다

요구 강도를 읽는 법

요구 강도를 나타내는 낱말은 RFC 2119 가 정의하고 RFC 8174 가 갱신했습니다. 둘을 묶은 것이 BCP(Best Current Practice) 14 입니다.

낱말 뜻
MUST 명세의 절대적 요구사항입니다. REQUIRED · SHALL 과 같은 뜻입니다
MUST NOT 명세의 절대적 금지사항입니다. SHALL NOT 과 같은 뜻입니다
SHOULD RECOMMENDED 와 같은 뜻입니다. 무시할 타당한 이유가 있을 수 있지만, 다른 길을 고르기 전에 그 함의를 전부 이해하고 신중히 저울질해야 합니다
SHOULD NOT NOT RECOMMENDED 와 같은 뜻입니다. 그 동작이 받아들일 만할 타당한 이유가 있을 수 있지만, 그렇게 구현하기 전에 함의를 이해하고 신중히 저울질해야 합니다

대문자로 쓰였을 때만 이 뜻이 걸립니다. 본문에 소문자로 섞인 "must" 나 "should" 는 요구 강도가 아닙니다.

MUST 와 SHOULD 가 갈리는 자리

절 번호와 함께 실물을 봅니다. 문서 이름 없이 절 번호만 적힌 것은 RFC 6749 의 절입니다. 대부분 MUST 로 못 박혀 있고, 강도를 낮춘 이유를 명세가 직접 적어 둔 곳은 리다이렉션 엔드포인트의 전송 보호 하나뿐입니다. 본문 여러 절에서 강도만 말하고 넘어간 대목의 출처도 아래 뒤쪽에 모았습니다.

자리 절 요구 강도
인가 엔드포인트의 전송 보호 §3.1 인가 엔드포인트로 요청을 보낼 때 인가 서버는 TLS 사용을 요구해야 합니다(MUST)
토큰 엔드포인트의 전송 보호 §3.2 토큰 엔드포인트로 요청을 보낼 때 인가 서버는 TLS 사용을 요구해야 합니다(MUST)
리다이렉션 엔드포인트의 전송 보호 §3.1.2.1 응답 타입이 code 나 token 일 때, 또는 리다이렉션 요청이 민감한 자격증명을 열린 망으로 나르게 될 때 TLS 사용을 요구하는 것이 좋습니다(SHOULD)
리다이렉션 엔드포인트 URI 의 꼴 §3.1.2 절대 URI 여야 합니다(MUST). 프래그먼트 컴포넌트가 들어가서는 안 됩니다(MUST NOT)
리다이렉션 엔드포인트 등록 §3.1.2.2 공개 클라이언트와 임플리싯 그랜트를 쓰는 기밀 클라이언트에는 등록을 요구해야 합니다(MUST). 그 밖의 모든 클라이언트에는 요구하는 것이 좋습니다(SHOULD)
리다이렉션 URI 대조 §3.1.2.3 인가 요청에 리다이렉션 URI 가 실려 오면 등록된 URI 중 적어도 하나와 비교해 맞춰야 합니다(MUST). 전체 URI 를 등록했다면 단순 문자열 비교로 대조해야 합니다(MUST)
잘못된 리다이렉션 URI §3.1.2.4 자원 소유자에게 오류를 알리는 것이 좋고(SHOULD), 잘못된 리다이렉션 URI 로 사용자 에이전트를 자동으로 되돌려서는 안 됩니다(MUST NOT)
리다이렉션 응답의 제3자 스크립트 §3.1.2.5 클라이언트는 리다이렉션 엔드포인트 응답에 제3자 스크립트를 넣지 않는 것이 좋습니다(SHOULD NOT). 넣는다면 자기 스크립트가 먼저 실행되도록 보장해야 합니다(MUST)
state 파라미터 §4.1.1 RECOMMENDED 입니다. 사이트 간 요청 위조를 막는 데 쓰는 것이 좋습니다(SHOULD)
클라이언트 자격증명 그랜트의 대상 §4.4 기밀 클라이언트만 써야 합니다(MUST)
토큰을 담은 응답의 캐시 §5.1 토큰이나 자격증명이나 그 밖의 민감 정보가 들어간 응답에는 Cache-Control: no-store 와 Pragma: no-cache 를 넣어야 합니다(MUST)
클릭재킹 대응 §10.13 최종 사용자 인가를 요청할 때 네이티브 애플리케이션은 앱 안에 브라우저를 심는 대신 외부 브라우저를 쓰는 것이 좋습니다(SHOULD)
토큰 요청을 받은 인가 서버가 할 일 §4.1.3 클라이언트 인증 · 인가 코드 검증을 비롯한 다섯 가지를 해야 합니다(MUST). 위 「예시」의 「토큰 요청」이 그 다섯입니다
보호 자원 요청에 토큰을 싣는 법 RFC 6750 §2.1 클라이언트는 Authorization: Bearer 헤더를 쓰는 것이 좋고(SHOULD), 자원 서버는 이 방법을 지원해야 합니다(MUST)
임플리싯 그랜트 RFC 9700 §2.1.2 쓰지 않는 것이 좋습니다(SHOULD NOT). 대신 code 응답 타입을 쓰는 것이 좋습니다(SHOULD)
자원 소유자 비밀번호 자격증명 그랜트 RFC 9700 §2.4 쓰면 안 됩니다(MUST NOT). 위의 임플리싯 그랜트보다 한 단계 센 강도입니다
PKCE 의 변환 방식 RFC 7636 §4.2 클라이언트가 S256 을 쓸 수 있으면 반드시 S256 을 써야 합니다(MUST)

리다이렉션 엔드포인트에 TLS 를 강제하지 않은 것은, 집필 시점에 클라이언트에 TLS 배치를 요구하는 것이 많은 클라이언트 개발자에게 상당한 장벽이었기 때문입니다. TLS 를 쓸 수 없으면 인가 서버는 리다이렉션 전에 그 엔드포인트가 안전하지 않다고 자원 소유자에게 경고하는 것이 좋습니다(SHOULD). 전송 계층 보안이 없으면 클라이언트와 그 클라이언트가 닿을 수 있는 보호된 자원의 보안에 심각한 영향이 갈 수 있습니다. 클라이언트가 최종 사용자 인증을 인가 절차에 맡겨 쓸 때 특히 그렇습니다.

관련 항목

명세가 정의한 역할과 값

자원 소유자 · 자원 서버 · 클라이언트 · 인가 서버 · 접근 토큰 · 리프레시 토큰 · 인가 코드 · 인가 그랜트 · scope · state 파라미터 · 리다이렉션 URI · 기밀 클라이언트 · 공개 클라이언트 · 최종 사용자 · 사용자 에이전트

서버 쪽 진입점

인가 엔드포인트 · 토큰 엔드포인트 · 리다이렉션 엔드포인트 · 토큰 폐기 · 토큰 인트로스펙션 · 인가 서버 메타데이터

문서

RFC 6749 · RFC 6750 · RFC 9700 · RFC 7636 · RFC 8628 · RFC 8252 · RFC 7009 · RFC 7662 · RFC 8414 · RFC 2119 · RFC 8174 · OAuth 2.1 · OAuth 1.0

함께 조합해 실무 시스템을 이루는 인접 표준

Bearer 토큰 · PKCE · OpenID Connect · SAML · JWT

이 명세가 기대는 하부 기술

TLS · HTTP Basic 인증 · base64url

이 명세가 곁들이는 배경 개념

인증과 인가 · 디바이스 플로우 · 송신자 제한 토큰

터지는 것과 그 이름

인가 코드 가로채기 공격 · 접근 토큰 유출 · 접근 토큰 재생 · 접근 토큰 주입 · 오픈 리다이렉터 · 클릭재킹 · CSRF

다른 이름: OAuth2 · OAuth 2