[카카오 로그인] REST API 는 성공했는데 JS SDK 사용시 발생하는 문제 문의드립니다

SDK : kakao_js_sdk/2.7.1
디벨로퍼스 앱ID : 1055754
Vue + Springboot 3 + Spring Security 6

안녕하세요.
카카오 로그인 관련 문의드립니다.

인앱 Webview 에서 동작하는 앱을 개발하고 있습니다.

REST API 방식으로는 로그인에 성공했는데,
Webview 에서는 카카오톡으로 간편로그인이 안된다는 걸 알게돼서
JS SDK 를 사용하는 방식으로 변경을 진행 중입니다.

frontend SDK 호출 로직

  var redirectUri = window.location.origin;
  if (redirectUri.includes("localhost") || redirectUri.includes("192.168")) {
    redirectUri = `http://${window.location.hostname}:8082`;
  }
  redirectUri += "/login/oauth2/code/kakao";

  const state = Util.generateRandomStr(16);
  Util.setCookie("state", state, 300);

  Kakao.Auth.authorize({
    redirectUri,
    state,
  });

위와 같이 frontend 에서 호출하면 backend 에 아래 요청이 들어오는데,
이후 에러로 진행이 안되고 있습니다.

backend 에 들어오는 요청
GET /login/oauth2/code/kakao?code=qYjhyfi498g7z_w0oKyZIxjMzXSr51P-CcfGRSYMo8aOYlF3rEoerZV2jDQKKcleAAABjpnbuEOoblpFv_zasg&state=lugzo9xl-3wnrhqs

backend 에서 발생하는 에러 로그
onAuthenticationFailure()
[authorization_request_not_found]
org.springframework.security.oauth2.core.OAuth2AuthenticationException: [authorization_request_not_found]

에러나는 곳을 살펴보면,
Spring Security 에서 OAuth2AuthorizationRequest 관련 처리를 못해서 발생하는데요,

REST API 방식에서는 kauth.kakao.com/oauth/authorize 요청을 backend 에서 하면서 이 때 session 에 OAuth2AuthorizationRequest 를 저장해놓기 때문에 정상동작 하는데,

JS SDK 를 사용하면 kauth.kakao.com/oauth/authorize 요청이 SDK 에 의해 브라우저에서 발생하면서 redirect uri 로 code 가 전달되는 시점에는 OAuth2AuthorizationRequest 처리가 안되는 것으로 이해하고 있습니다.

JS SDK 를 사용한다면 누구나 이 문제를 겪을텐데, 관련 질문이나 설명 글을 찾을 수가 없어서,
제가 뭔가 놓치고 있거나 이해를 잘못하고 있을거 같습니다… ^^;;

해결하기 위해 보통 어떻게 처리하면 되는지 문의드립니다.
그리고 webview 앱 환경에서 JS SDK 를 쓰면 카카오톡으로 간편 로그인이 되는건 맞는건지도 문의드립니다.

감사합니다.

안녕하세요.

JS SDK를 사용하시면 카카오톡이 설치되어 있는 경우 톡으로 로그인하는 간편 로그인 기능이 제공 됩니다.


해당 오류는 Spring Security의 정상적인 오류입니다.

Spring Security Oauth2.0 모듈에서 제공하는 인가요청 주소(ex, /oauth2/authorization/kakao )를 통해 로그인이 시작되지 않은채로 redirect_uri로 이동하면 OAuth2LoginAuthenticationFilter 에서 오류를 발생 시킵니다.

OAuth2LoginAuthenticationFilter 에서는 OAuth2AuthorizationRequestRedirectFilter를 통해 인가요청 되었는지 확인하는 로직이 있습니다.

이를 해결하고자 하신다면 redirect_uri 로직을 직접 구현하시거나 OAuth2LoginAuthenticationFilter를 재정의 부탁드립니다.

답변 감사합니다.

oauth2 플로우를 전체적으로 공부해보고 있습니다.

Spring Security 의 OAuth2LoginAuthenticationFilter 에서는 state 를 검증하고 있는데요,
(session 의 state 와 request 의 query 에 있는 state)

OAuth2LoginAuthenticationFilter 를 재정의한다면 이 state 를 어떻게 검증하는게 맞을지 문의드립니다.

지정한 redirect uri 에 request 만 한 번 오는거라 이걸 검증을 할 수 있는건지,
제가 뭔가 잘못 생각하고 있는건지 알 수가 없네용…

안녕하세요.

서비스는 Spring Oauth2 인증 요청 프로세스를 타지 않기 때문에 OAuth2LoginAuthenticationFilter에서 state 파라미터 검증이 불가 합니다.

때문에, OAuth2LoginAuthenticationFilter 재정의 하시기 보다 redirect_uri 처리 로직을 직접 구현하시는 것을 권장 드립니다.

답변 감사합니다.

redirect uri 를 직접 구현한다는 건 Spring Security 의 정해진 redirect uri 형식 말고,
제 서비스에 mapping 을 하나 추가해서 진행하는걸 말씀하시는거 맞나요?

맞다면,
이렇게 구현해도 마찬가지로 state 와 같은 보안을 위한 검증은 할 수 없는게 아닌지 문의드립니다.

직접 구현하자니 property 를 읽고 요청하는 등 일이 너무 커질거 같아서 겁이 앞서네요… ^^;

RequestMapping으로 처리하실수도 있고 커스텀 Authentication 필터를 생성하여 처리하실 수도 있습니다. 서비스측 인가 처리에 알맞게 선택하여 구현 하시면됩니다.

state 파라미터는 서버측에서 인가코드 요청 시 발급하고 redirect_uri 에서 검증 하셔야 합니다.

state 파라미터는 서버측에서 인가코드 요청 시 발급하고 redirect_uri 에서 검증 하셔야 합니다.
—> JS SDK 사용에 대한 문의인데요, 인가코드 요청은 JS SDK 에서 하는 것 아닌가요?

JS SDK로 state 파라미터를 전달할 수 있습니다.

Kakao.Auth.authorize({
  state: ''
  ...
})

즉, 서비스에서 state 검사를 하고자 하시는 경우 카카오 로그인 기능이 있는 페이지에 접근시 state 값을 생성 및 파라미터로 전달하여 이후 인가코드가 전달될때 검증하시면 됩니다.

답변 감사합니다. 제가 충분히 이해를 못하고 있는 듯 합니다 ^^;;

state 의 검증 주체는 서비스 측 서버인데요,
서비스측 서버로 state 가 전달되는 때는 redirect uri 가 호출되는 시점, 즉 인가코드가 전달될 때로 이해하고 있습니다.

순서로 보면 아래와 같은데요,

  1. 서비스 client 에서 JS SDK 로 Kakao.Auth.authorize() 를 실행
  2. Kakao 서버에서 인가 코드 생성
  3. 서비스 서버의 redirect uri 로 인가 코드가 포함된 요청이 들어옴

이대로라면 서비스 서버에는 state 3에서 처음으로 state 를 받기 때문에 원본 state 가 뭔지 알 수 없는게 아닌가 합니다.
REST API 방식에서는 인가 코드 요청을 서비스 서버에서 했기 때문에 이미 서버가 state 를 알고 있는거고요.

계속해서 oauth 방식에 대한 이해가 부족한 상태에서 질문을 드리는거 같아 죄송합니다. ^^;;;;

state 의 발급 및 검증 주체는 서비스 입니다.
Spring에서 제공하는 oauth2 provider를 사용한 인가코드 주소 접근 시, spring이 자동으로 state 파라미터를 생성하여 카카오의 인가코드 주소로 전달하고 카카오는 카카오 로그인 후 이를 redirect_uri로 전달하게 됩니다. redirect_uri 진입 시 spring의 OAuth2LoginAuthenticationFilter에서 이 값을 검증하게 됩니다.

Spring에서 제공하는 oauth2 provider를 사용한 인가코드 주소 접근 시,


제가 이해 못하고 있는 부분이 이 부분같은데요,
REST API 방식으로 할 때는 서비스 클라이언트에서 서비스 서버의 “Spring에서 제공하는 oauth2 provider를 사용한 인가코드 주소 접근” 을 하고,
JS SDK 방식으로 할 때는 서비스 클라이언트에서 JS SDK 를 통해 카카오 서버로 요청이 가는게 아닌가요?

이후 서비스 서버의 redirect uri 가 호출되는거고요.

안녕하세요.

REST API 인가코드 요청 시, 카카오의 인가코드 요청 주소를 직접 호출 하는 방식과 Spring에서 제공하는 방식을 사용할 수 있습니다.

  1. JS SDK 사용 또는 카카오 인가코드 요청 주소 직접 사용
    JS SDK는 카카오톡으로 실행 가능한 경우 톡 호출이 되며 그렇지 않은경우 카카오 인가코드 요청 주소로 이동함
  2. Spring 제공 주소 사용

이 중 두번째 방식은 Spring에서 state 값 생성 후, 카카오 인가코드 요청주소로 이동 시킵니다.

제가 궁금한 부분이 해결이 안돼서 재차 문의드립니다. ^^;;
아래와 같이 이해한 부분을 정리해보았는데요, 틀린 부분이 있는지 확인 좀 부탁드립니다.

Kakao.Auth.authorize({
  state: ''
  ...
})

위와 같이 제 클라이언트에서 생성한 state 를 추가할 수 있습니다.
위 코드를 실행하면 SDK 에서 카카오의 인가코드 요청 주소를 직접 호출하게 됩니다.

이후 카카오 서버는 제가 설정한 Redirec URI 로 POST 요청을 보냅니다. 이 때 state 가 함께 옵니다.
하지만 이 시점에서 제 서버(서비스 서버)에는 원본 state 가 없습니다.
(왜냐하면 state 를 클라이언트에서 생성했고 서버로 전달한 적이 없음)

즉, SDK 를 사용할 경우에는
클라이언트 - 서버 간 state 를 공유할 수 있는 방법을 마련하던가,
state 를 검증하지 않는 것으로 Spring Security 쪽 처리 로직을 작성해야 합니다.

state 값은 백엔드 서버에서 발급 하셔야만 합니다. OAuth2 스팩에서 필수 요구사항은 아닙니다.

state는 CSRF 방어를 위해 사용됩니다.
즉, 서비스측 서버를 통해 인가요청이 이루어 졌는지 검증하는 보안 기술로
클라이언트에서 발급하여 서버로 전달하여 검증하는 것은 이미 보안 목적을 달성하기 어려운 상태 입니다.

Spring에서 제공하는 메커니즘을 그대로 사용하고자 하시기에 state 파라미터에 집중하는 것으로 이해 되는데요.
JS SDK + Spring Security 조합에서 state 파라미터 사용을 위해 보다 간단히 커스터마징하고자 하신다면 AuthorizationRequestRepository 를 재정의 하시어 state 파라미터를 핸들링 하시는 것을 고려해 보시는것도 좋을것 같습니다.

친절한 답변 감사합니다!
즐거운 주말 되세요~!