"Google 로 로그인" 버튼 뒤에 OAuth 2.0 이 있다. 이 가이드는 OAuth 가 실제로 무엇인지 (인증이 아니라 위임), 4 가지 표준 흐름의 차이, 왜 implicit 흐름이 PKCE 로 대체됐는지, refresh token 과 scope 의 의미를 정리한다. RFC 6749 + RFC 7636 (PKCE) + 현재 best practice.
OAuth 가 푸는 문제
이메일 마케팅 도구가 사용자의 Gmail 연락처를 가져오고 싶다고 하자. 예전 방식은 사용자 비밀번호를 직접 받아 Google 에 로그인하는 것 — 끔찍 하다. 비밀번호가 제 3 자에게 노출되고, 모든 권한을 다 가짐.
OAuth 의 답:
- 제 3 자 (마케팅 도구) 가 사용자에게 "Google 권한 받아 와" 요청.
- 사용자는 Google 로 가서 로그인 + "연락처 read 만" 동의.
- Google 이 마케팅 도구에 access token 발급.
- 마케팅 도구는 token 으로 연락처 API 호출. 비밀번호는 모름.
핵심 키워드: 위임 (delegation). 사용자가 자기 권한 일부 를 제 3 자에게 위임. OAuth 자체는 인증 (사용자가 누구인지 확인) 이 아니다. 인증이 필요하면 OpenID Connect (OAuth 위에 정체성 정보 추가) 를 쓴다.
네 가지 역할
- Resource Owner — 사용자 (데이터의 주인).
- Client — 데이터를 빌리려는 앱 (마케팅 도구).
- Authorization Server — 권한을 발급하는 곳 (Google OAuth 서버).
- Resource Server — 보호된 API (Google Contacts API).
Google·GitHub·Auth0 같은 IdP 는 Authorization Server + Resource Server 역할을 동시에 한다.
4 가지 표준 Grant Type
1. Authorization Code (권장 — 서버 사이드 앱)
가장 흔한 흐름. 백엔드가 있는 웹 앱.
- Client → 사용자를 Auth Server 의
/authorize로 보냄. query:response_type=code,client_id,redirect_uri,scope,state. - 사용자 동의 후 Auth Server 가
redirect_uri?code=...&state=...로 다시 보냄. - Client (서버) 가
/token에 POST —code+client_secret교환 → access token + (옵션) refresh token. - access token 으로 API 호출 (
Authorization: Bearer ...).
장점: client_secret 이 서버에만 있어 노출 0. token 이 브라 우저 URL 에 절대 안 나타남.
state 파라미터는 CSRF 방어 — 랜덤 값 발급 + 콜백 시 검증. URL 파서 로 callback URL 의 query 를 분해해 보면 흐름이 직관적.
2. Authorization Code + PKCE (권장 — SPA·모바일)
SPA·모바일은 client_secret 을 안전하게 보관 못 한다 (코드에 박히면 누구나 추출). PKCE (Proof Key for Code Exchange, RFC 7636) 가 해결.
- Client 가
code_verifier(43-128 자 랜덤) 를 만들고code_challenge = SHA256(code_verifier)계산. /authorize호출 시code_challenge+code_challenge_method=S256함께 전송.- callback 후
/token호출 시code_verifier(원본) 를 함께 보냄. Auth Server 가 SHA256 다시 계산해 일치 확인.
효과: 공격자가 code 를 가로채도 code_verifier 없으면 token 교환 불가. client_secret 없이도 안전한 코드 교환. SHA-256 계산을 직접 보고 싶다면 SHA 해시 에 verifier 를 넣어 결과를 challenge 와 비교할 수 있다.
3. Client Credentials (server-to-server)
사용자 개입 없는 흐름. Cron job 이 자체 권한으로 API 호출. Client 가 직접 /token 에 grant_type=client_credentials + client_id + client_secret 보내고 access token 받음.
스코프는 클라이언트에 미리 부여된 권한 안에서만. 사용자 데이터가 아니라 애플리케이션 자체의 데이터에 접근할 때 (예: 자체 분석 데이터, admin API).
4. Refresh Token
엄밀히 grant type 이 아니라 access token 갱신용 별도 grant.
POST /token
grant_type=refresh_token
refresh_token=<refresh_token>
client_id=...access token 은 짧게 (5~60 분), refresh 는 길게 (수일~수주). access 가 만료되면 refresh 로 새로 받음. refresh 자체가 노출되면 더 위험하므로 보안 저장소 (HttpOnly Secure 쿠키, OS Keychain 등) 에 보관.
(deprecated) Implicit / Resource Owner Password
둘 다 OAuth 2.1 draft 에서 제거. Implicit 은 token 이 URL fragment 로 직접 노출 → CSRF/XSS 위험. Password Grant 는 사용자 비밀번호를 client 가 받음 → OAuth 의 본 취지 위반. 새 시스템에서 쓰지 말 것.
Scope — 권한 단위
scope 파라미터로 위임 범위를 명시. 공급자마다 다름:
- Google:
https://www.googleapis.com/auth/contacts.readonly - GitHub:
repo,user:email,workflow - OpenID Connect:
openid profile email
원칙: 최소 권한. read 만 필요하면 read scope 만 요청. 사용자 동의 화면이 짧을수록 conversion 도 높다.
Access Token — 두 가지 형식
JWT (self-contained)
토큰 자체에 사용자 정보 + 권한 박힘. Resource Server 는 JWT 디코더 같은 라이브러리로 검증만 하면 됨. DB 조회 0. Auth0, Cognito, Firebase 등이 사용.
직접 발급해 보고 싶다면 JWT 생성기 (HMAC) 로 payload + 시크릿 + alg 입력해 토큰을 만들 수 있다. HS256 의 서명은 HMAC 생성기 의 결과와 동일.
Opaque (introspection)
토큰이 그냥 랜덤 문자열. Resource Server 가 매 요청마다 Auth Server 의 /introspect 엔드포인트에 물어봄. Auth Server 가 즉시 회수 가능 (장점). 매번 조회 비용 (단점).
OpenID Connect — OAuth + 정체성
OAuth 는 권한 위임만. "사용자가 누구인지" 알려면 OIDC.
scope=openid가 OIDC 활성화.- access token 외에 ID token (JWT) 추가 발급. 사용자 ID·이메일·이름 등 표준 claim 박힘.
/userinfo엔드포인트로 추가 사용자 정보 조회 가능.
"Sign in with Google" 의 정체는 OIDC. Google 의 ID token 을 받아 우리 앱의 사용자 세션을 만든다.
흔한 함정
1. state 검증 누락
callback 에서 state 를 검증 안 하면 CSRF — 공격자가 자신의 code 를 사용자에게 redirect 시켜 사용자 계정에 자기 OAuth 를 연결. 반드시 발급 시 저장 + 콜백 시 일치 확인.
2. PKCE 없는 SPA
client_secret 을 SPA 에 박는 건 즉시 노출. code_verifier 가 동적 생성되므로 안전. 모든 SPA 는 PKCE 강제.
3. token 을 localStorage 에 저장
XSS 한 번이면 모든 token 탈취. HttpOnly Secure 쿠키 권장. 또는 메모리 + refresh 만 쿠키 패턴.
4. redirect_uri 와일드카드
Auth Server 에 redirect_uri 정확히 등록. example.com/* 같은 와일드카드는 open redirect → token 탈취 취약.
5. scope 과다 요청
"그냥 다 받아두자" → 동의 화면이 무서워 보이고 사용자 이탈. 필요한 scope 만 단계적으로 요청.
6. refresh token rotation 미적용
refresh token 사용 시 새 refresh + 이전 token 무효화 패턴 권장 (RFC 6749 § 6 의 sender-constrained / Refresh Token Rotation). 탈취 감지 가능.
실전 — Google OAuth 시작하기
- Google Cloud Console 에서 OAuth client 만들기 → client_id + secret 발급. redirect_uri 등록.
- 사용자 클릭 시
https://accounts.google.com/o/oauth2/v2/auth으로 redirect (위 query 포함). - callback 에서
code받고https://oauth2.googleapis.com/tokenPOST 로 교환. - access_token 으로
https://www.googleapis.com/oauth2/v3/userinfo호출.
라이브러리: Auth.js (NextAuth), Passport, Authlib (Python), Spring Security 등이 위 단계를 모두 추상화.
요약
- OAuth 2.0 = 위임. 인증이 필요하면 OIDC 추가.
- 서버 앱 = Authorization Code, SPA/모바일 = + PKCE, 서버끼리 = Client Credentials.
- Implicit / Password Grant 는 deprecated. 새 시스템에서 금지.
- access token 짧게, refresh 로 갱신. token 은 HttpOnly 쿠키 권장.
- scope 최소화.
state+ PKCE + redirect_uri 정확 매칭이 기본 방어.