[OAuth 2.0] Authorization Code Grant 전체 흐름 한 눈에 보기

이 글에서는 RFC 6749를 기반으로 Authorization Code Grant의 전체 흐름을 본 후 각 엔드포인트가 어떤 책임을 가지는지 단계적으로 분석해보려고 한다.

 

Authorization Code Grant의 전체 흐름은 아래와 같다.

OAuth 2.0 Authorization code 큰 흐름

  • Authorization Code Grant는 크게 두 개의 엔드포인트를 중심으로 동작한다. 바로 Authorization Endpoint와 Token Endpoint이다.

 

세부적으로 Authorization Endpoint는 다음과 같다.

  1. 클라이언트 식별 및 redirect_uri 검증
  2. 사용자 인증
  3. Authorization Code 발급

→ 이 단계는 브라우저를 통해 사용자와 직접 상호 작용하는 구간이다.

 

세부적으로 Authorization Token Endpoint는 다음과 같다.

  1. 인가 코드 및 리다이렉션 URI
  2. 액세스 토큰(선택적 리프레시 토큰 포함)

→ 이 단계는 서버 간 통신으로 이루어지며, 사용자는 더 이상 개입하지 않는다.

이제 각 상세인 1번 ~ 5번 사이를 상세하게 분석해보자

 

Authorization Endpoint(1~3번)

1. 클라이언트 식별 및 redirect_uri 검증

 

위 그림은 사용자가 클라이언트 애플리케이션에서 로그인 버튼을 누른 시점부터 브라우저가 인가 서버의 /oauth2/authorize 엔드포인트로 요청을 전달하는 순간까지의 흐름이다.

 

이 과정은 크게 3단계로 나눌 수 있다.

  1. 사용자가 클라이언트에서 로그인 버튼을 클릭한다.
<https://client.example.com/login>

 

사용자는 자신이 이용하려는 서비스(클라이언트 서버)에 접속해 로그인 버튼을 클릭한다.

 

이 단계에서는 아직 OAuth 요청이 발생하지 않는다. 단순히 클라이언트 애플리케이션 내부 동작일 뿐이다.

 

 

2. 클라이언트 서버가 인가 서버로 리다이렉트를 지시한다.

클라이언트 서버는 브라우저에게 다음과 같은 302 응답을 보낸다.

HTTP/1.1 302 Found
Location: <https://auth.example.com/oauth2/authorize>
    ?response_type=code
    &client_id=test-client
    &redirect_uri=https://client.example.com/callback
    &scope=read
    &state=xyz

여기서 중요한 점은

  • 브라우저가 직접 인가 서버로 이동한다는 것
  • 클라이언트 서버가 토큰을 직접 요청하는 것이 아니다

이 단계는 OAuth 요청을 생성하는 단계이다. 아직 인가 서버는 아무 검증도 수행하지 않는다.

 

3. 브라우저가 인가 서버의 /oauth2/authorize로 요청을 보낸다.

브라우저는 302 응답을 받고 자동으로 다음 요청을 보낸다.

GET /oauth2/authorize
?response_type=code
&client_id=...
&redirect_uri=...
&scope=...
&state=...

이 순간부터 Authorization Endpoint의 실제 처리가 시작된다.

 

인가 서버는 여기서 다음을 검증한다

  • client_id가 등록된 클라이언트인지
  • redirect_uri가 사전 등록된 값과 일치하는지
  • response_type이 code인지
  • scope가 허용된 범위인지

 

이 검증이 통과해야만 다음 단계(사용자 인증)로 진행된다.

 

2. 사용자 인증

2.1 사용자 인증

앞선 단계에서 인가 서버는 클라이언트를 식별하고 redirect_uri를 검증했다.

 

이제 다음으로 필요한 것은 사용자가 누구인지 확인하는 과정, 즉 사용자 인증(Authentication)이다. OAuth는 이름은 Authorization이지만 Authorization Code를 발급하기 전에 반드시 사용자 인증이 선행되어야 한다.

 

아래 그림은 이 인증 과정을 HTTP 흐름 관점에서 보여준다.

 

4. 로그인 페이지로 리다이렉트

인가 서버는 현재 사용자 세션이 없음을 확인하고 다음과 같이 응답한다.

HTTP/1.1 302 Found
Location: /login

이는 다음을 의미한다

  • 사용자가 아직 인증되지 않음
  • 먼저 로그인 과정을 거쳐야 함

즉, Authorization Endpoint는 인증을 직접 수행하지 않고, 로그인 엔드포인트로 흐름을 넘긴다.

 

5. 브라우저가 /login 요청

브라우저는 302 응답을 보고 자동으로 /login으로 이동한다.

GET /login
Host: auth.example.com

이 과정은 브라우저가 자동으로 처리한다.

 

 

6. 로그인 페이지 HTML 응답

인가 서버는 로그인 폼을 포함한 HTML을 반환한다.

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

<form action="/login" method="post">
    ...
</form>

이 단계에서 사용자에게 로그인 화면이 표시된다.

 

7. 로그인 페이지가 사용자에게 보임

이제 사용자는 실제 로그인 화면을 보게 된다.

이 시점은 OAuth 흐름이라기보다는 일반적인 웹 로그인 화면과 동일하다.

 

8. 사용자가 로그인 정보 입력 후 제출

사용자가 아이디와 비밀번호를 입력하고 로그인 버튼을 클릭한다.

 

9. 로그인 요청 (POST /login)

브라우저는 다음과 같은 요청을 인가 서버로 전송한다.

POST /login
Content-Type: application/x-www-form-urlencoded

username=user&password=1234

여기서 인가 서버는 실제 인증을 수행한다

  • 사용자 존재 여부 확인
  • 비밀번호 검증
  • 인증 성공 여부 판단

 

이 단계가 바로 Authentication(인증)이다.

 

 

10. 로그인 성공 후 원래 인가 요청으로 복귀

인증이 성공하면 인가 서버는 다음과 같이 응답한다.

HTTP/1.1 302 Found
Location: /oauth2/authorize?response_type=code...
Set-Cookie: JSESSIONID=...

여기에는 두 가지 중요한 의미가 있다

 

  • 세션 생성

Set-Cookie 헤더를 통해 로그인 세션이 생성된다.

  • 원래 Authorization Endpoint로 복귀

처음 요청했던 /oauth2/authorize로 다시 돌아간다. 이제 사용자는 인증된 상태이므로 Authorization Endpoint는 다음 단계인 사용자 동의(Consent) 로 진행할 수 있다.

 

2.2 사용자 동의

 

사용자 인증이 완료되면, 인가 서버는 다음을 확인한다

→ 이 사용자가 해당 클라이언트에게 요청된 scope에 대해 이미 동의했는가?

 

동의 여부에 따라 흐름이 달라진다.

 

10. 다시 /oauth2/authorize 요청

로그인 성공 후 브라우저는 원래 요청이었던 /oauth2/authorize로 다시 이동한다.

GET /oauth2/authorize
?response_type=code
&client_id=test-client
&redirect_uri=https://client.example.com/callback
&scope=read
&state=xyz
Cookie: JSESSIONID=...

이제 사용자는 인증된 상태다.

 

인가 서버는

  • 동의가 이미 저장되어 있는지 확인한다.
  • 없다면 동의 화면으로 이동시킨다.

 

12. 동의 화면으로 리다이렉트

동의가 아직 이루어지지 않았다면, 인가 서버는 다음과 같이 응답한다.

HTTP/1.1 302 Found
Location: /oauth2/consent
?client_id=test-client
&scope=read
&state=xyz

이 단계는 사용자에게 권한 요청 내용을 보여주기 위한 준비 단계이다.

 

13. 브라우저가 동의 페이지 요청

GET /oauth2/consent
?client_id=test-client
&scope=read
&state=xyz
Cookie: JSESSIONID=...

이 요청을 통해 동의 화면을 받아오게 된다.

 

14. 동의 화면 HTML 응답

인가 서버는 동의 화면을 HTML로 반환한다.

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

<h1>권한 요청</h1>
<form method="post" action="/oauth2/authorize">
    <input type="hidden" name="client_id" value="test-client" />
    <input type="hidden" name="scope" value="read" />
    <input type="hidden" name="state" value="xyz" />
    ...
</form>

이 화면은 다음을 사용자에게 보여준다

  • 어떤 클라이언트가
  • 어떤 권한(scope)을 요청하는지

 

15. 동의 화면이 사용자에게 표시됨

사용자는 이제 이 클라이언트가 내 리소스에 접근하도록 허용할 것인지 선택해야 한다.

 

16. 사용자가 동의 버튼 클릭

사용자가 동의(approve) 버튼을 누르면 브라우저는 다음 요청을 보낸다.

 

17. 동의 제출(POST /oauth2/authorize)

POST /oauth2/authorize
Content-Type: application/x-www-form-urlencoded
Cookie: JSESSIONID=...

client_id=test-client
&scope=read
&state=xyz
&consent=approve

이제 인가 서버는 다음을 수행한다:

  • 동의 여부 확인
  • Authorization Code 생성
  • redirect_uri로 리다이렉트

 

3. Authorization Code 발급

사용자가 동의(Consent)를 완료하면 인가 서버는 최종적으로 Authorization Code를 발급한다. 이 단계는 Authorization Endpoint의 마지막 책임이다.

 

18. 인가 코드 발급 및 redirect_uri로 리다이렉트

인가 서버는 다음과 같이 응답한다.

HTTP/1.1 302 Found
Location: <https://client.example.com/callback>
?code=SplxlOBeZQQYb6WxSbIA
&state=xyz

이 응답에는 중요한 의미가 담겨 있다.

 

  • Authorization Code 발급 → code 파라미터가 바로 Authorization Code이다.
    • 일회성 값
    • 짧은 유효시간
    • 이후 Token Endpoint에서 Access Token으로 교환됨
  • state 값 유지 → 요청 시 전달했던 state 값이 그대로 반환된다. 이는 CSRF 공격을 방지하기 위한 장치다.
  • Access Token은 아직 발급되지 않음 → 이 시점에서는 Access Token이 아니라 Authorization Code만 전달된다. 이 설계가 바로 Authorization Code Grant의 핵심 보안 구조다.

 

19. 브라우저가 클라이언트의 callback 엔드포인트로 요청

브라우저는 302 응답을 보고 자동으로 클라이언트 서버로 이동한다.

GET /callback
?code=SplxlOBeZQQYb6WxSbIA
&state=xyz

이제 흐름은 인가 서버에서 클라이언트 서버로 넘어간다.

클라이언트 서버는 이 code를 받아 다음 단계인 Token Endpoint 요청을 준비하게 된다.

 

Authorization Token Endpoint(4~5번)

4. 인가 코드 및 리다이렉션 URI, 5. 액세스 토큰(선택적 리프레시 토큰 포함)

Token Endpoint (/oauth2/token)

  • 앞 단계에서 클라이언트는 Authorization Code를 전달받았다. 이제 이 코드를 사용해 Access Token을 발급받아야 한다. 이 과정은 브라우저가 아니라 클라이언트 서버와 인가 서버 간의 직접 통신으로 이루어진다.

 

  1. 클라이언트가 Token Endpoint로 요청

클라이언트 서버는 다음과 같은 POST 요청을 보낸다.

POST /oauth2/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=SplxlOBeZQQYb6WxSbIA
&redirect_uri=https://client.example.com/callback

이 요청에는 몇 가지 중요한 요소가 있다.

 

→ 클라이언트 인증(Authorization 헤더)

Authorization: Basic base64(client_id:client_secret)

인가 서버는 이 값을 통해 요청을 보낸 클라이언트가 누구인지, 등록된 client_secret과 일치하는지를 검증한다.

즉, 이 단계에서는 사용자 인증이 아니라 클라이언트 인증이 수행된다.

 

grant_type=authorization_code

이 요청이 어떤 흐름에 해당하는지 명시한다.

  • authorization_code → 인가 코드 교환
  • 다른 값일 경우 다른 Grant Type 처리

 

Authorization Code 검증

인가 서버는 다음을 확인한다

  • code가 유효한가?
  • 이미 사용된 코드가 아닌가?
  • redirect_uri가 처음 요청과 동일한가?
  • 코드가 만료되지 않았는가?

이 검증이 통과해야 토큰이 발급된다.

 

2. 인가 서버의 응답(토큰 발급)

검증이 성공하면, 인가 서버는 JSON 형태로 응답한다.

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "def50200a2b3...",
  "scope": "read"
}

 

응답 필드 설명

  • access_token

리소스 서버에 접근할 때 사용하는 실제 토큰이다.

Authorization: Bearer <access_token>

형태로 API 요청 시 사용된다.

 

  • token_type → 대부분 Bearer 타입을 사용한다.
  • expires_in → Access Token의 유효시간(초 단위)이다.
  • refresh_token (선택적) → Access Token이 만료되었을 때, 재발급받기 위해 사용하는 토큰이다. == Public Client의 경우 발급되지 않을 수도 있다.
  • scope → 최종적으로 허용된 권한 범위이다.

 

리소스 요청

 

  1. 클라이언트의 리소스 요청을 통해 엑세스 토큰을 헤더로 전달
  2. 리소스 서버의 리소스 응답

 

결론

  • Authorization Endpoint 단계

인가 서버는 /oauth2/authorize 요청을 받으면 다음을 수행한다.

  1. 클라이언트 식별 및 redirect_uri 검증
  2. 사용자 인증 (로그인)
  3. 사용자 동의 (Consent)
  4. Authorization Code 발급

이 단계의 결과는 Authorization Code이다.

중요한 점은 Access Token은 아직 발급되지 않는다. 브라우저를 통해 전달되는 것은 오직 Authorization Code뿐이다.

 

  • Token Endpoint 단계

클라이언트 서버는 Authorization Code를 사용해 /oauth2/token 엔드포인트로 직접 요청을 보낸다.

이때 인가 서버는

  • 클라이언트 인증 (client_id, client_secret)
  • Authorization Code 검증
  • redirect_uri 일치 여부 확인
  • 코드 재사용 여부 확인

을 수행한 후 Access Token을 발급한다. 이 과정은 서버 간 통신으로 이루어진다.