한양과학기술고등학교

에듀테크 프로젝트 대시보드

OAuth 2.0 인가 코드 흐름

OAuth 2.0 인가 코드 흐름

"구글로 로그인" 버튼을 누르면 내 비밀번호를 넘기지 않고도 어떻게 로그인이 될까? — 위임 인가(delegated authorization)의 핵심 프로토콜

한 줄 요약

OAuth 2.0은 비밀번호를 공유하지 않고, 제3의 앱에게 "내 계정의 일부 권한만" 잠깐 빌려주는 표준이다. 사용자는 신뢰하는 인가 서버(구글 등)에서만 로그인하고, 앱은 그 대가로 짧은 수명의 액세스 토큰을 받는다.

1. 등장 인물 4명

Resource Owner 사용자 (나) 보호된 자원(내 프로필, 사진)의 주인. 권한을 위임할지 결정한다.
Client 제3자 앱 내 자원을 쓰고 싶어하는 서비스. 예: 사진 인쇄 사이트가 구글 포토에 접근.
Authorization Server 인가 서버 사용자를 인증하고 동의를 받아 인가 코드와 토큰을 발급. 예: accounts.google.com
Resource Server 자원 서버 실제 API. 액세스 토큰을 검사해 데이터를 내준다. 예: Google Photos API

2. 인가 코드 흐름 — 단계별 시뮬레이터

아래 "다음 단계" 버튼을 눌러 브라우저 리다이렉트가 어떻게 이어지는지 따라가 보자. 파란 테두리는 지금 요청을 받는 쪽이다.

사용자 브라우저
Client 앱
인가 서버
자원 서버
STEP 0 / 7
사용자가 Client 앱에서 "구글로 로그인" 버튼을 누르기 직전. 시작하려면 "다음 단계"를 누르세요.

3. 왜 "코드"를 한 번 더 교환할까?

인가 서버는 토큰을 브라우저 주소창(리다이렉트 URL)에 바로 실어 보내지 않고, 일회용 인가 코드만 보낸다. 그 코드를 Client의 백엔드 서버가 자신의 client_secret과 함께 뒤에서(back-channel) 토큰으로 바꾼다.

PKCE (Proof Key for Code Exchange) — 모바일 앱·SPA처럼 client_secret을 숨길 수 없는 "공개 클라이언트"는 대신 매 요청마다 임의의 code_verifier를 만들고 그 해시(code_challenge)를 1단계에 보낸다. 토큰 교환 때 원본 code_verifier를 제시해야만 하므로, 코드를 가로채도 소용이 없다. 오늘날 PKCE는 모든 클라이언트에 권장된다.
# PKCE 값 생성 (브라우저에서) code_verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" # 43~128자 랜덤 code_challenge = BASE64URL( SHA256(code_verifier) ) = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM" # 1단계 인가 요청에 code_challenge 를 실어 보냄 # 5단계 토큰 교환에 code_verifier 원본을 제시 → 서버가 해시 비교

4. 실제 HTTP 요청/응답

1단계 — 인가 요청 (브라우저 리다이렉트)

GET https://accounts.google.com/o/oauth2/v2/auth? response_type=code &client_id=1234.apps.googleusercontent.com &redirect_uri=https://printphotos.com/callback &scope=openid%20profile%20photos.readonly &state=xyz-CSRF-랜덤값 &code_challenge=E9Melhoa2Owv...w-cM &code_challenge_method=S256

5단계 — 코드를 토큰으로 교환 (Client 백엔드 → 인가 서버)

POST https://oauth2.googleapis.com/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &code=4/0AeaYSH... (일회용) &redirect_uri=https://printphotos.com/callback &client_id=1234.apps.googleusercontent.com &client_secret=GOCSPX-•••••••• &code_verifier=dBjftJeZ4CVP-mB92K27uh... ── 응답 ── { "access_token": "ya29.a0Af...", // 자원 서버에 제시, 수명 ~1시간 "expires_in": 3600, "refresh_token": "1//0gEo...", // 만료 시 새 토큰 발급용, 장수명 "scope": "openid profile photos.readonly", "token_type": "Bearer" }

6단계 — API 호출 (Client → 자원 서버)

GET https://photoslibrary.googleapis.com/v1/albums Authorization: Bearer ya29.a0Af...

5. 토큰의 두 종류

액세스 토큰 (Access Token)리프레시 토큰 (Refresh Token)
용도API를 부를 때마다 제시액세스 토큰이 만료되면 새로 발급
수명짧다 (수 분~1시간)길다 (수일~무기한, 철회 전까지)
노출 위험자주 오가지만 금방 만료거의 안 움직이고 서버에 안전 보관
탈취 시피해가 만료까지로 제한치명적 → 반드시 서버 측 저장·회전

6. 흔한 오해 정리

핵심 정리

  • 사용자는 신뢰하는 인가 서버에서만 비밀번호를 입력한다. Client는 비밀번호를 절대 못 본다.
  • 브라우저(front-channel)로는 일회용 코드만, 서버(back-channel)로는 비밀과 함께 토큰을 교환.
  • state로 CSRF 방어, PKCE로 코드 가로채기 방어.
  • 액세스 토큰은 짧게, 리프레시 토큰은 서버에 안전하게.