🧐 🤔 컴퓨터 옆 메모장 🙄 😙

OAuth2

2024. 11. 25. Kim Evergood이가 씀

목적

전통적 인증/인가(아이디와 비밀번호 입력으로 이용자가 그 서비스에서 가능한 전권 획득)와 달리; 제3의 앱에게 제한된 권한을 부여한다.

리소스오너, 리소스서버, 인증서버, 클라이언트의 네 가지 역할이 등장하여 최종적으로 클라이언트가 리소스서버에서 제한적으로 리소스를 얻는다.

예를 들어 어느 달력 앱에서 구글 로그인을 통해 구글 캘린더에서 일정 정보를 가져와 연동한다고 치자. 달력 앱에서는 구글 사용자의 일정 외에 받은 메일 목록, 유튜브 채널 주소 등을 알 필요가 없고; 사용자 입장에서도 그런 정보를 제공하지 않도록 제한하는 편이 보안에 좋다.

용어 정리

주고받는 것

리소스resource: 결국 얻으려는 데이터. 예시에서 구글 캘린더 내의 일정 정보.

자격증명credential: (영어사전) 신용 증명물, 증명서; (대사·공사·사절 등의) 신임장; (일반적으로) 증명서, 보증서, 자격 인정서, 추천장.

권한부여authorization grant: 리소스에 접근해도 된다고 허락하는 리소스오너의 인가를 나타내는 자격증명. 클라이언트는 이것을 사용하여 액세스토큰을 얻는다.

액세스토큰access token: 인증서버가 클라이언트에게 발급하여, 클라이언트가 리소스에 접근할 때 사용되는 자격증명.

리프레시토큰refresh token: 인증서버가 클라이언트에게 발급하여, 클라이언트가 새 액세스토큰을 얻을 때 사용되는 자격증명.

역할

리소스오너resource owner: 리소스로의 접근권한을 부여해줄 능력을 가진 놈. 예시에서 이용자인 나.

리소스서버resource server: 보호된 자원을 관리하는 놈. 액세스토큰을 써서 요청에 응답. 예시에서 구글 캘린더 서비스.

클라이언트client: 리소스오너를 대신하여 리소스를 요청하는 놈. 예시에서 달력 앱.

인증서버authorization server: 리소스오너가 인증된 경우 액세스토큰을 발급해주는 놈. 예시에서 구글 계정 서비스.

기타

스코프scope: 요청 혹은 부여되는 권한의 범위

어설션assertion: 특정 사용자 혹은 클라이언트의 정보를 담은 것. 서명되어 전달되고 수신자가 검토 후 신뢰한다. 대표적으로 JWT Bearer Token으로 클라이언트가 인증서버에 자신의 신원을 증명한다.

PKCEProof Key for Code Exchange#

(사전작업) 클라이언트 등록

인증서버는 등록된 클라이언트만을 받아들인다. 프로토콜 개시 전; 클라이언트를 인증서버에 등록해야 한다. 그 방법은 이 사양에서 논외인데; 그 둘의 상호작용이 직접적이어야 하는 건 아니다. 아래 구현 예:

  • 클라이언트가 자가발행한 어설션을 인증서버에 제출
  • 제3의 기관에서 인증서를 받아 인증버서에 제출
  • 인증서버가 신뢰하는 클라이언트의 목록을 보유
클라이언트의 유형

confidential: 클라이언트의 자격증명을 비밀로 유지할 수 있다.(예: 웹사이트)
public: 클라이언트의 자격증명을 비밀로 유지하지 못한다.(예: 일반 사용자의 기기에서 실행되는 앱)

클라이언트 개발자의 할일
  • 클라이언트 유형 판별
  • 클라이언트 리디렉션 끝점 URI를 인증서버에 제공
  • 인증서버가 요구하는 기타 정보 제공

프로토콜

클라이언트는 권한부여요청을 직접 할 수도 있고, 리소스오너를 인증서버로 디렉션시킬 수도 있다.

받은 액세스토큰이 올바른 토큰인 줄을 어떻게 아는가? 인증서버와 리소스서버의 상호작용은 이 스펙이 정의하는 바가 아니다. 한 가지 구현으로서, 인증서버와 리소스서버는 같을 수 있다.

권한 부여 방식

위 다이어그램에서 주황색으로 표시된 부분을 설명한다.

Authorization Code Grant

리디렉션 기반으로 진행된다. 액세스토큰과 리프레시토큰을 모두 얻는다. credential 클라이언트에 최적화.

여기서 리디렉션을 생략하여 표현하면 아래와 같다.

PKCE

OAuth 2.1에서부터 PKCE를 이용하게 되었다.

  1. 랜덤한 문자열 code_verifier를 생성한다. 매 권한부여시마다 새 랜덤값을 쓴다.
  2. code_verifier로부터 (SHA256 등의 함수를 거쳐) code_challenge 값을 생성하고 권한부여 요청시 파라미터로 보낸다.
  3. 액세스토큰 요청시 code_verifier를 파라미터로 보낸다. 인증서버는 아까의 이것과 아까의 code_challenge가 쌍이 맞는지 확인한다.

결론적으로; 인증한 놈과 액세스토큰 요청하는 놈이 동일한지 확인한다. OAuth2.0에서 권한부여코드가 URL에 노출되는 보안적 문제를 보완하려고 추가되었다.

Implicit Grant

리디렉션 기반으로 진행된다. 클라이언트가 리디렉션 URI를 통해 권한부여코드를 얻지 않고 바로 액세스토큰을 얻으며 리프레시토큰을 못 얻는다.
클라이언트 인증 없이 리소스오너의 존재와 리디렉션 URI에 의존.
URI로 액세스토큰이 전달되므로 같은 기기를 사용하는 사람이나 프로그램에게 액세스토큰이 노출될 수 있다.
public 클라이언트에 최적화.

OAuth 2.1에서 제거됨.

Resource Owner Password Credentials Grant

클라이언트가 리소스소유저의 비밀번호를 직접 받아서 사용한다.

다른 방식을 못 쓸 때, 리소스오너가 클라이언트를 신뢰할 때에만 허용돼야 한다.

OAuth 2.1에서 제거됨.

Client Credentials Grant

클라이언트 인증만으로 액세스토큰을 얻는다.
반드시 confidential 클라이언트에서만 사용돼야 한다.

Refresh Token Grant

이전에 받은 권한부여를 기반으로 새 액세스토큰을 얻는다.
리프레시토큰은 반드시 비밀로 유지되어야 한다.

OAuth 2.1에서 추가중.

끝점

인증서버에서
  • 권한부여 끝점: 리소스오너로부터 권한부여를 받으려고 클라이언트에서 인증서버로 리디렉션되는 곳

  • 토큰 끝점: 클라이언트가 권한부여와 액세스토큰을 교환하는 곳.

클라이언트에서
  • 리디렉션 끝점: 권한부여 자격증명을 포함한 응답과 함께 인증서버에서 클라이언트로 리디렉션되는 곳.

권한부여 요청

권한부여 끝점에 보내는 요청.

쿼리파라미터
  • response_type(필수)
  • client_id(필수): 클라이언트 식별자
  • redirect_uri
  • scope: 요청할 액세스 허용 범위
  • state
  • code_challenge(PKCE 사용 시 필수): PKCE로 인증 보안을 강화하기 위한 값
  • code_challenge_method(PKCE 사용 시 필수): code_challenge를 생성하는 방법을 지정
    • S256(권장): SHA256 해싱을 사용.
    • plain: 보안상 권장되지 않는다.

액세스토큰 형식

인증서버의 액세스토큰 발급 성공 응답

상태코드

200

바디 파라미터
  • access_token(필수): 발급된 액세스토큰. 문자열.
  • token_type(필수): 토큰 유형.
  • expires_in: 액세스토큰의 남은 유효기간 (초 단위).
  • refresh_token: 리프레시토큰.
  • scope: 액세스 허용 범위. 대소문자구별문자열, 공백으로 구분, 순서 무관.
예시 (Body 부분, JSON 형식)
{
    "access_token": "a7v2J4GOfUZCa3K9TaS6xU7lW6c",
    "token_type": "bearer",
    "refresh_token": "5xJvQqiXWiASW60qTXX4P-UcvsI",
    "expires_in": 762400,
    "scope": "read write"
}
{
  "access_token": "ya29.a0AeECVZcuhDvsuih3uU2aekI7tuP7LcvkuKjbe8NdkE9gl91X-vmvJMT1u1LGLSAWxd1xpk0Ca4njMMbQmIw97hfejkfPtiHXiK4BKFIDRizEMR2s5LcISjZQ2sgZuDXHyR_TA2TD5y_L8v4LL2QWen3YOKv998svkjdskjFjn32q3lkNLFQHGX2MiedJ11fv7MsJoIY-x2wAIBA0171",
  "scope": "https://www.googleapis.com/auth/userinfo.profile openid https://www.googleapis.com/auth/userinfo.email",
  "id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
  "token_type": "Bearer",
  "expires_in": 3582
}

참고

RFC 6749 - The OAuth 2.0 Authorization Framework
(draft) - The OAuth 2.1 Authorization Framework

728x90