OAuth — state · PKCE · OIDC
OAuth — state · PKCE · OIDC
OAuth 2.0 은 사용자의 자격을 노출하지 않고 다른 시스템에 권한 위임을 허용하는 표준. 위에 OpenID Connect 가 인증 (누구인가) 을 얹습니다. 이 글은 Authorization Code Flow + PKCE · state 와 nonce · 스코프 설계 · Google · Kakao · Naver · GitHub OAuth 의 사실 차이.
1. 표준에 대한 이야기
- OAuth 2.0 — RFC 6749 (2012). 권한 위임의 프레임워크. 자격 (비밀번호) 을 직접 공유하지 않고 토큰으로 권한을 부여.
- OpenID Connect (OIDC) — 2014. OAuth 2.0 위에 ID Token 과 사용자 정보 엔드포인트를 더해 인증을 표준화. ID Token 은 JWT.
- PKCE — RFC 7636 (2015). 공개 클라이언트 (SPA · 모바일) 의 인증 코드 가로채기를 차단하기 위한 확장.
- OAuth 2.1 — 진행 중인 통합 사양. 안전한 디폴트 (필수 PKCE · implicit flow 제거 등) 를 묶음.
2. Authorization Code + PKCE 흐름
- 클라이언트가
code_verifier(랜덤 문자열) 생성.code_challenge = base64url(sha256(verifier)). /authorize?response_type=code&client_id=...&redirect_uri=...&scope=...&state=...&code_challenge=...&code_challenge_method=S256으로 리다이렉트.- 사용자 동의.
- IdP 가
redirect_uri?code=...&state=...로 돌려보냄. - 클라이언트가 state 를 이전 값과 비교 (CSRF 검증).
- 클라이언트가
/token에code+code_verifier제출. - IdP 가
code_challenge와code_verifier의 일치를 확인 후 토큰 발급.
3. state 파라미터
state 는 CSRF 방지가 핵심. 공격자가 자기 인증 코드를 피해자에게 리다이렉트해 피해자가 공격자 계정으로 로그인하는 시나리오 (로그인 CSRF) 를 막음.
- 클라이언트가 임의의 nonce 같은 값을 만들어 세션에 저장.
/authorize에state로 전달.- 콜백에서 받은
state가 세션의 값과 같은지 확인. - 다르면 거부.
state 는 충돌 가능성이 낮은 충분히 긴 랜덤 값.
4. nonce (OIDC)
OIDC 의 ID Token 안에 포함되는 값. ID Token 의 재사용 (replay) 방지가 목적.
- 클라이언트가 인증 요청에 nonce 보냄.
- IdP 가 받은 nonce 를 ID Token 클레임으로 돌려줌.
- 클라이언트가 자기 nonce 와 일치하는지 검증.
state 와 nonce 는 다른 목적으로 둘 다 필요합니다.
5. PKCE 의 의미
코드 가로채기 공격은 모바일 · SPA 에서 클라이언트 시크릿이 안전하게 보관되지 않는 문제에서 출발. PKCE 는 클라이언트 시크릿 없이도 같은 출처의 클라이언트만 토큰을 받을 수 있게 — 코드를 가로채도 code_verifier 가 없으면 토큰 교환이 실패.
OAuth 2.0 에서 SPA · 모바일에 옵션이었으나, OAuth 2.1 에서 모든 클라이언트에 사실상 필수.
6. 폐기 권고된 흐름
Implicit Flow — response_type=token 으로 액세스 토큰을 URL 프래그먼트로 직접 받음. 코드 단계 없음 → 토큰이 브라우저 히스토리 · 로그에 노출 위험. 현재는 권장 안 됨, OAuth 2.1 초안에서 제거.
Resource Owner Password Credentials (ROPC) — 사용자가 비밀번호를 클라이언트에 직접 입력. OAuth 의 핵심 동기 (비밀번호를 공유하지 않음) 에 어긋남. 폐기 권고.
Device Authorization Grant — RFC 8628 (2019). TV · IoT 등 입력이 제한된 기기에서 별도 디바이스로 인증을 진행하는 흐름. device_code · user_code 가 핵심.
7. 스코프 설계
- 표준 OIDC 스코프 —
openid·profile·email·address·phone. - 공급자별 — Google
https://www.googleapis.com/auth/calendar.readonly처럼 URL 형식이 흔함. - 자체 API — 자기 도메인 + 권한 이름 (
api.read:posts).
권고:
- 최소 권한 원칙. 처음에 다 요구하지 말고 필요할 때 incremental authorization.
- 사용자에게 동의 화면이 명료하도록.
- 서버에서 다시 검증. 클라이언트 토큰의 스코프를 그대로 신뢰하지 않음.
8. 공급자별 사실 차이
Google — OIDC 표준 충실. discovery 문서 https://accounts.google.com/.well-known/openid-configuration. ID Token RS256, JWKS 회전. incremental authorization 가능. consent 가 첫 동의 후 재요청 시 생략 가능 (prompt=consent 로 강제).
Kakao — OAuth 2.0 기반. OIDC 도 지원 (2022 년경 추가, ID Token 발급). Kakao Developers 콘솔에서 동의 항목을 별도로 설정해야 사용자에게 표시. 일부 정보 (이메일 등) 는 사용자가 비공개 처리하면 비어 올 수 있음.
Naver — OAuth 2.0. OIDC 표준 ID Token 은 미제공. 사용자 정보 API (/v1/nid/me) 로 별도 조회. 한국 사용자를 대상으로 하는 자리에서 자주.
GitHub — OAuth 2.0 (OIDC 는 GitHub Actions OIDC 등 한정 자리). 일반 로그인은 OIDC 가 아닌 OAuth 2.0. 사용자 정보는 GET /user. 스코프가 단순 (read:user · repo) 하지만 강력 — repo 는 모든 저장소 접근.
Apple Sign in — OIDC 표준. ID Token 사용. 이메일을 사용자가 가릴 수 있음 (private relay). 첫 로그인 응답에만 이름이 포함되는 점이 알려져 있음 — 백엔드가 그때 저장해야.
9. 안전한 디폴트
- 인증 흐름은 Authorization Code + PKCE 하나.
- redirect_uri 는 사전 등록된 정확한 값만 (와일드카드 위험).
- state · nonce 검증 빠뜨리지 않기.
- access · refresh 분리, 짧은 access TTL (01-jwt-rotation).
- 토큰 저장은 httpOnly 쿠키 또는 안전한 키스토어.
- 백엔드는 서명된 ID Token 의 클레임 검증 —
iss·aud·exp·nonce.
10. 자주 걸리는 자리
state 누락 · 고정값 — CSRF 차단이 사실상 사라짐. 매 요청 새 값.
redirect_uri 의 느슨함 — 와일드카드 · prefix 매칭은 위험. 정확한 매칭만.
public client 가 시크릿을 가짐 — 모바일 · SPA 의 클라이언트 시크릿은 결국 노출. PKCE 로 대체.
OIDC 의 aud 검증 누락 — 다른 클라이언트로 발급된 ID Token 을 그대로 받아들이면 토큰 혼용 사고.
사용자 정보 API 응답 변동 — 공급자가 응답 키 이름 · null 처리를 변경할 수 있음. 방어 코드.
이메일 신뢰 — GitHub 같은 공급자의 이메일이 미인증일 수 있음. 자체 이메일 검증 흐름 별도.
로그아웃의 의미 — 우리 세션을 끊는 것과 IdP 세션을 끊는 것은 다름. RP-Initiated Logout 표준은 OIDC 의 별도 사양.
콜백의 동시 실행 — 사용자가 여러 탭에서 OAuth 를 시작하면 state 가 섞임. 상태 저장 키 분리.
하고픈 말
OAuth 2.0 + PKCE + state + nonce 의 표준 흐름이 안전한 디폴트의 토대입니다. 여기서 더 나은 자리는 OIDC 의 ID Token 검증 (iss · aud · exp · nonce) 까지 내려가는 것. 공급자별 사소한 차이 (Naver 의 OIDC 미제공, GitHub 의 미인증 이메일) 가 실수의 자리이니 자체 이메일 검증 흐름이 보조로 자주 함께 따라옵니다.
Next
- rate-limit-redis
- input-validation-zod
RFC 6749 OAuth 2.0 · RFC 7636 PKCE · RFC 9700 OAuth 2.0 BCP · OpenID Connect Core · Google Identity · Kakao 로그인 · Naver 로그인 API · GitHub OAuth 를 참고합니다.