OAuth 2.0 흐름을 알기 위해서 몇 가지 알아야 할 키워드가 있다.
OAuth 2.0 Rolse
- Resource Owner (자원 소유자)
- 보호된 자원에 대한 접근 권한을 부여할 수 있는 주체, 사용자로서 계정의 일부에 대한 접근 권한을 부여하는 사람
- 사용자를 대신하여 작동하려는 모든 클라이언트는 먼저 사용자의 허가를 받아야 한다.
- 즉, 서비스를 사용하려는 사용자이다.
- Resource Server (자원 서버)
- 타사 애플리케이션에서 접근하는 사용자의 자원이 포함된 서버를 의미
- 액세스 토큰을 수락 및 검증할 수 있어야 하며 권한 체계에 따라 요청을 승인할 수 있어야 한다.
- 카카오, 네이버, 구글, Github를 예로 들 수 있다.
- Authorization Server (인가 서버)
- 클라이언트가 사용자 계정에 대한 동의 및 접근을 요청할 때 상호 작용하는 서버로서 클라이언트의 권한 부여 요청을 승인하거나 거부하는 서버
- 사용자가 클라이언트에게 권한 부여 요청을 승인한 후 access token을 클라이언트에게 부여하는 역할
- Keycloak, Spring Authorization Server, Okta, 카카오, 네이버, 구글, Github 등이 있다.
- Client (클라이언트)
- 사용자를 대신하여 권한을 부여받아 사용자의 리소스에 접근하려는 애플리케이션
- 사용자를 권한 부여 서버로 안내하거나 사용자의 상호 작용 없이 권한 부여 서버로부터 직접 권한을 얻을 수 있다.
이렇게 나열해 놨지만 감이 오지 않는다. 그림을 통해서 알아보자
Client (클라이언트)
위 내용을 보면 클라이언트는 권한을 부여받아 사용자의 리소스에 접근하려는 애플리케이션 이라고 설명했다.
클라이언트는 자원 서버에 접근할 수 있는 주체라고 볼 수 있다.
그럼 자원 서버에 접근하기 위해서는 증명할 수 있는 수단이 필요한데, 그것을 OAuth 2.0에서는 AccessToken이라고 부른다.
OAuth 2.0은 인증이 아닌 인가를 위한 프로토콜이다.
인증은 Open ID Connect(OIDC) 참고

클라이언트가 인가를 요청하고 어떤 처리가 완료되면 AccessToken을 발급받는 방식이다.
여기서 어떤 처리는 이후에 설명할 OAuth 2.0 Grant Types으로 다시 설명할 예정이다.
공개적으로 요청하고 액세스 토큰을 발급받는 것으로 보이는데 실제로 브라우저(SPA)에서 실행되는 JavaScript 애플리케이션, Android 또는 IOS 모바일 앱, 데스크톱에서 실행되는 기본 앱, IoT/임베디드 장치에서 실행되는 애플리케이션 등에서 사용할 수 있는 방식이다. 이런 방식을 Public (공개) 클라이언트라고 부른다.

공개 클라이언트와 다르게 공란이었던 back channel 영역이 채워져 있는 것을 볼 수 있는데,
공개 클라이언트 방식에서 보안을 위해 추가된 스텝이라고 볼 수 있다.
AccessToken을 공개 채널에서 받지 않고 back channel에서 받아 처리하는 방식이다.
여기까지 깊게 이해할 필요 없이 클라이언트는 AccessToken을 발급받는 주체라고 이해하면 편하다.
OAuth 2.0 Grant Types
위에서 잠깐 언급했는데 AccessToken을 어떻게 발급받는지에 대한 방식을 말한다.
여러 가지 방식이 있지만 대표적으로 Authorization Code Grant Type 방식을 많이 사용한다.
Authorization Code Grant Type 흐름만 알게 된다면 나머지 권한 부여 타입에 대해서는 검색을 통해 쉽게 이해할 수 있을 거라고 생각된다.
Authorization Code Grant Type

쉽게 접근할 수 있게 예를 들어 설명하겠다.
사용자(Resource Owner)가 마이페이지 기능이 있는 웹 애플리케이션(Client)에 접속해 있다.
신규 기능으로 현재 계정의 프로필 사진 이미지를 카카오 프로필과 동일하게 설정하는 연동 기능이 추가되었다고 하자
웹 애플리케이션 서버(Client)는 사용자(Resource Owner)에게 동의를 받으면 카카오 서버(Resource Server)에 접근하고 동의받은 사용자의 프로필 사진을 불러온다.
- 사용자는 카카오 프로필 연동 요청 버튼을 누른다.
- 카카오 프로필 동의를 받아야 하므로 동의 페이지를 제공한다.
- 사용자는 동의를 얻기 위해 해당 페이지를 요청하고
- 카카오 인가 서버에서는 로그인 화면을 안내한다.
- 프로필을 연동할 카카오 계정 로그인을 한다.
- 로그인이 성공되면 카카오는 쿼리 파라미터로 code 값 담아 웹 애플리케이션 서버로 돌아가라는 리다이렉트를 응답하고 보낸다.
- 사용자의 브라우저는 리다이렉트 요청을 받았기 때문에 클라이언트에게 code 값을 같이 요청한다.
- 클라이언트는 code 값을 받아 카카오 인가서버에게 요청하면
- 검증 후
- code 값이 올바르다고 판단되면 사용자의 리소스에 접근 가능한 Access 토큰을 발급받게 된다.
- 클라이언트는 발급받은 토큰으로 카카오 리소스 서버에게 프로필 사진을 요청하고
- Access 토큰이 올바르면
- 프로필 사진을 받아 비즈니스 로직을 수행하게 된다.
- 서버는 사용자에게 연동된 프로필을 제공한다.
문제점 - 1
중간에 해커가 code 값을 탈취하게 된다면, 정상 사용자보다 먼저 AccessToken을 요청할 수 있게된다.
즉, 해커가 AccessToken을 얻을 수 있다는 문제가 있다.
해결 방안
Secret Key
위 문제점을 해결하기 위해 8번 과정에서 고유한 Secret 키를 같이 보내게 된다.
인가 서버는 클라이언트의 Secret 키를 확인하고, 검증 후 토큰을 발급한다.
이렇게 된다면 해커가 code 값을 가로채도 Secret 키를 모르기 때문에 code 값 탈취는 의미가 없어진다.
Secret Key만 잘 보관된다면 충분히 안전한 방법이다.
아래 사진은 카카오에서 제공하는 Client Secret 값이다.

문제점 - 2
Secret Key 방식은 Client가 백엔드 서버일 때 가능하다. (back channel에서 교환 하기 때문에)
Client가 React나 Vue 같은 SPA 환경의 서버라면 Secret Key는 무의미하다.
이유는 개발자 도구만으로도 JS 코드를 훤히 들여다볼 수 있으므로 마음만 먹으면 코드를 분석해 Secret Key를 알아낼 수 있다.
몇몇 블로그를 보면 SPA 환경에서는 Client Secret 방식을 '사용안함'으로 설정하라는 글들이 보이는데 권장하지 않는 방법이다.
해결 방안
PKCE(Proof Key for Code Exchange)
OAuth 2.0 Grant Types 중 하나이다.
PKCE 뜻을 보면 Code 교환을 위한 증명 키이다.
PKCE는 위에서 살펴본 Authorization Code Grant Type 흐름과 유사하지만, 약간의 차이점이 있다.
3번과 8번에서 보내는 쿼리 파라미터가 몇개 추가되었다.
어떻게 보안을 지켰는지 하나씩 살펴보자
카카오 로그인 페이지에 요청하기 전 클라이언트(SPA)는 code_verifier 값과 code_challenge 값을 생성해야 한다.
- code_verifier: 48 ~ 128 길이의 랜덤 문자열이다.
- randomString() ➡️ abcd(48 길이)
- code_challenge: code_verifier를 특정 해시 함수(SHA256)로 해싱 후 base64로 인코딩 한 값이다.
- sha256('abcd') ➡️ 9C9A433B67154B248B93BF805DD...
- base64('9C9A433B67154B248B93BF805DD...')
3번 과정에서
https://AUTH_URL?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&code_challenge=${codeChallenge}&code_challenge_method=S256
생성한 code_challenge와 사용한 해시 함수(S256)를 쿼리 파라미터에 담아 요청한다.
쭉 흐름을 타다가 8번 과정에서는
code=${authCode}&code_verifier=${codeVerifier}
authorization code값과 code_challenge 값의 재료인 code_verifier를 쿼리 파라미터에 담아 AccessToken을 요청한다.
인가서버에서는 받은 code_verifier를 SHA256 방식으로 해싱하고
그 결과를 Base64로 인코딩해서 code_challenge를 직접 만들어 본다.
인가서버는 직접 만든 code_challenge 값을 3번 단계에서 받은 code_challenge 값과 비교한다.
만약 두 값이 일치한다면, 정상적인 인가 과정을 거친 사용자로 간주하고 클라이언트에게 AccessToken을 발급한다.
만약 해커가 중간에 authorization code 값을 가로채더라도 code_challenge의 원본 값인
code_verifier를 유추할 수 없기 때문에 안전하게 지킬 수 있다.
이렇게 PKCE 방식을 사용함으로써, SPA 환경에서 Secret Key 없이 일회용 코드만으로
Authorization Code Grant Type을 안전하게 사용할 수 있게 된다.