본문으로 건너뛰기

KR Kakao 인증 API 엔드포인트

목차

접근 권한 매트릭스

엔드포인트Public앱 토큰사용자 JWT내부 운영자비고
GET /auth/oauth/kakaostate/nonce 생성, CSRF 방지 헤더 필요
GET /auth/oauth/kakao/callbackKakao redirect 전용, idempotency 필수
POST /auth/oauth/kakao/exchange앱 토큰 인증, 신규 사용자는 pending 상태 유지
POST /auth/oauth/kakao/provisioning/completeappToken 기반 온보딩 제출
POST /auth/oauth/kakao/unlink사용자 JWT 필요, CSRF 헤더 검증

참고: Kakao 로그인 기능은 feature.auth.kakao 플래그가 활성화된 환경에서만 노출됩니다.

카카오 OAuth 엔드포인트

📌 시퀀스 다이어그램: 카카오 로그인 전체 흐름

인가 URL 초기화 GET /auth/oauth/kakao

  • 역할: Kakao OAuth 2.0 Authorization Code Grant를 시작할 인가 URL을 반환하고 state/nonce를 초기화합니다.
  • 보안 포인트
    • TimeMachine 기준 TTL 10분으로 Redis auth:kr:kakao:session:*에 state/nonce를 저장합니다.
    • 이 단계를 생략하면 서버가 state/nonce를 제어할 수 없어 콜백 위·변조와 재전송 공격을 차단할 수 없습니다.
    • 클라이언트는 반환된 state를 안전하게 보관하고 콜백에서 동일한 값을 전달해야 합니다.
  • 응답 헤더: Set-Cookie: kakao_auth_state=...; HttpOnly; Secure, Cache-Control: no-store

콜백 처리 GET /auth/oauth/kakao/callback

  • 역할: Kakao authorization code를 수신해 state 검증, 토큰 교환, JWT 발급을 트리거합니다.
  • 주요 작업
    1. Redis에서 state/nonce 및 idempotency 키(auth:kr:kakao:idempotency:{state}) 확인
    2. Kakao 토큰 엔드포인트(https://kauth.kakao.com/oauth/token) 호출로 access/refresh 토큰 수신
    3. Auth CoreIssueSessionService 호출로 JWT 액세스/리프레시 토큰 발급
    4. ExternalIdentityLink 갱신 및 KakaoAuthCompletedEvent 발행
  • 응답: 기본적으로 302 리다이렉트(app://auth/callback?...)로 JWT 전달, 필요 시 JSON 응답 대체 가능
  • 보안 포인트: 웹 클라이언트는 POST /auth/oauth/kakao/exchange(AppTokenGuard 적용)를 통해 code를 교환하며, 신규 사용자의 온보딩 pending 상태를 appToken으로 추적한다.

프로비저닝 제출 POST /auth/oauth/kakao/provisioning/complete

  • 역할: 신규 사용자 온보딩 폼을 접수하고, 프로비저닝 티켓을 단 한 번만 소비한다.
  • 주요 작업
    1. AppTokenGuard로 인증된 appToken의 JTI를 기반으로 pending 세션을 조회하고 재사용을 차단한다.
    2. 제출된 프로필 정보를 Redis auth:kr:kakao:provision-submission:{appTokenId}에 보관 (TTL 24시간)
    3. 운영 워크플로가 참고할 수 있도록 감사 로그, 이벤트 발행(향후 확장)
  • 응답: 202 Accepted + 접수 메시지, 400 KAKAO_PROVISIONING_SESSION_INVALID (세션 만료/중복)
  • 역할: 기존 Kakao 외부 식별자를 내부 사용자 계정에서 분리하고 토큰을 폐기합니다.
  • 요구 조건
    • 사용자 JWT 액세스 토큰 필수, CSRF 헤더 검증
    • unlink 사유(USER_REQUEST, DATA_PORTABILITY 등)를 요청 본문에 포함
  • 결과: ExternalAuthUnlinkedEvent 발행, Kakao 리프레시 토큰 폐기 처리

요청/응답 계약

GET /auth/oauth/kakao

// Response 200
{
"authorizationUrl": "https://kauth.kakao.com/oauth/authorize?...",
"state": "f1556c2f-...",
"expireAt": "2025-01-20T10:32:00.000Z"
}

GET /auth/oauth/kakao/callback

파라미터위치필수설명
codequeryKakao authorization code
statequeryRedis에 저장된 state 값
error, error_descriptionquery아니오Kakao에서 전달하는 오류 케이스
  • 성공 응답: 302 RedirectLocation: app://auth/callback?kakao=true&jwtAccessToken=...
  • 오류 응답: 400 Bad Request(state 불일치, code 만료), 503 Service Unavailable(Kakao API 장애, Retry-After 포함)

POST /auth/oauth/kakao/provisioning/complete

  • 헤더: Authorization: Bearer {appToken}
// Request
{
"name": "홍길동",
"organization": "SleepZ 운영팀",
"email": "operator@sleepz.dev"
}

// Response 202
{
"status": "accepted",
"message": "프로비저닝 요청이 접수되었습니다. 검토 후 이용 가능 여부를 안내드릴게요.",
"next": "/login?reason=session-required"
}
  • 프로비저닝 세션이 만료되었거나 이미 사용된 경우 400 KAKAO_PROVISIONING_SESSION_INVALID를 반환합니다.

POST /auth/oauth/kakao/unlink

// Request
{
"reason": "USER_REQUEST"
}

// Response 200
{
"status": "unlinked",
"unlinkedAt": "2025-01-20T10:40:00.000Z"
}
  • 사용자 JWT가 유효하지 않거나 CSRF 검증에 실패하면 401/403 응답을 반환합니다.

오류 코드

HTTP Status코드설명도메인 예외
400AUTH_KAKAO_STATE_MISMATCHstate 불일치KakaoStateMismatchError
400AUTH_KAKAO_CODE_EXPIREDauthorization code 만료KakaoAuthorizationCodeExpiredError
401AUTH_KAKAO_AUTHENTICATION_FAILEDKakao 인증 실패KakaoAuthenticationFailedError
409AUTH_KAKAO_ALREADY_LINKED다른 계정에 이미 연결됨KakaoIdentityAlreadyLinkedError
503AUTH_KAKAO_PROVIDER_DOWNKakao API 장애KakaoProviderUnavailableError
400KAKAO_PROVISIONING_SESSION_INVALID프로비저닝 세션 만료/중복 사용KakaoProvisioningStateInvalidError

모니터링 및 로그 포맷

  • 로그 태그: event=kakao_auth, stage=request|callback|unlink, correlationId, state
  • 메트릭: auth_kakao_login_latency_seconds, auth_kakao_callback_error_total
  • 모든 타임스탬프는 TimeMachine 기반 권한 있는 시각(requestedAt, completedAt)으로 기록합니다.

변경 이력

버전날짜작성자변경 내용
0.1.02025-10-20bok@weltcorp.com초기 엔드포인트 정의