사용자가 비밀번호를 주지 않고 제3자 앱에 자기 자원 접근 권한을 위임하는 프로토콜.
무엇을 해결하나
"이 앱이 내 구글 드라이브 파일을 읽게 하고 싶다"
❌ 구글 비밀번호를 앱에 알려 준다
- 앱이 메일도 읽고 계정도 삭제할 수 있다. 비밀번호를 바꿔야만 회수된다
✅ OAuth
- "드라이브 읽기 권한만" 위임. 언제든 회수 가능. 비밀번호는 안 넘어간다
네 역할
| 역할 | 누구 |
|---|---|
| Resource Owner | 사용자(나) |
| Client | 권한을 요청하는 앱 |
| Authorization Server | 구글 로그인 서버 |
| Resource Server | 구글 드라이브 API |
Authorization Code Grant — 표준 흐름
왜 코드를 한 번 거치나 — 핵심 설계
토큰을 바로 주지 않고 code를 먼저 주는 이유는
- redirect는 브라우저 주소창을 거친다
- 토큰을 직접 주면 URL·브라우저 기록·리퍼러에 남는다
code는
- 일회용이고 수명이 짧다(보통 10분)
- 토큰으로 바꾸려면 client_secret이 필요하다 (서버만 아는 값)
- 브라우저에서 code가 새어 나가도 토큰을 얻을 수 없다
PKCE — 이제는 모든 클라이언트에 권장
모바일 앱·SPA는 client_secret을 안전하게 보관할 수 없다(디컴파일하면 나온다).
그래서 매 요청마다 만드는 일회용 비밀을 쓴다.
-
① 클라이언트가 무작위 code_verifier 생성
-
② code_challenge = SHA256(code_verifier) 를 인증 요청에 첨부
-
③ 토큰 교환 시 원본 code_verifier 를 제시
-
④ 서버가 SHA256(verifier) == challenge 확인
-
공격자가 code를 가로채도 verifier를 모르면 토큰을 못 받는다
OAuth 2.1에서는 모든 흐름에 PKCE가 필수가 된다.
사라진/피해야 할 방식
| 방식 | 상태 |
|---|---|
| Implicit | 폐기. 토큰이 URL에 노출됨 → Authorization Code + PKCE로 대체 |
| Resource Owner Password | 폐기. 비밀번호를 앱에 넘기므로 OAuth의 취지에 반함 |
| Client Credentials | 유효. 서버 간 통신(사용자 없음) |
| Device Code | 유효. TV·CLI처럼 입력이 불편한 기기 |
scope — 최소 권한
- scope=drive.readonly — ← 읽기만
- scope=drive — ← 전체 접근 ✗ 과도하다
필요한 최소 범위만 요청한다. 과한 권한을 요구하면 사용자가 동의를 거부하기도 한다.
가장 중요한 오해 — OAuth는 인증이 아니다
OAuth 2.0 = 인가(Authorization) 프로토콜 "이 앱이 내 자원에 접근해도 된다"는 위임이지, "이 사용자가 누구인지"를 알려 주는 규격이 아니다
Access Token만으로 로그인 처리를 하면 위험하다. 그 토큰이 우리 앱을 위해 발급된 것인지 확인할 방법이 없어, 다른 앱용 토큰을 가져와 남의 계정으로 로그인할 수 있다(confused deputy).
→ 로그인이 목적이면 OIDC를 써야 한다. OIDC는 OAuth 위에
id_token(누구인지를 담은 서명된 JWT)을 추가한 규격이다.
이 구분을 짚으면 면접에서 확실히 좋은 인상을 준다.