카카오로그인 Response 401 UNAUTHORIZED 오류

앱ID : 1038078

카카오 소셜 로그인 기능을 구현하고 있습니다.

카카오 로그인 페이지로 이동하여 로그인 정보 입력 > 동의항목 확인 후 동의하는 페이지까지는 정상적으로 뜨는 것을 확인했습니다.

동의하기 클릭 후 404 error로 페이지가 이동하고 있고,
인가 코드를 받은 후 토큰을 발급 받는 과정에서 오류가 발생하는 것으로 확인했습니다.

오류코드는 Response 401 UNAUTHORIZED 입니다.

관련 오류 로그

09:25:50,492 DEBUG [org.springframework.web.client.RestTemplate] (http-/0.0.0.0:18080-4) HTTP POST https://kauth.kakao.com/oauth/token
09:25:50,534 DEBUG [org.springframework.web.client.RestTemplate] (http-/0.0.0.0:18080-4) Accept=[application/json, application/*+json]
09:25:50,546 DEBUG [org.springframework.web.client.RestTemplate] (http-/0.0.0.0:18080-4) Writing [KakaoRequest [clientId=${정보보호}, redirectUri=http://localhost:18080/portal/login/oauth2/code/kakao.do, clientSecret=${정보보호}, responseType=null, scope=null, code=${정보보호}, accessType=null, grantType=authorization_code, state=null, includeGrantedScopes=null, loginHint=null, prompt=null]] with org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
09:25:51,157 DEBUG [org.springframework.web.client.RestTemplate] (http-/0.0.0.0:18080-4) Response 401 UNAUTHORIZED
09:25:51,169 DEBUG [org.springframework.web.servlet.DispatcherServlet] (http-/0.0.0.0:18080-4) Failed to complete request: org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 Unauthorized: [no body]
09:25:51,186 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/portal].[appServlet]] (http-/0.0.0.0:18080-4) JBWEB000236: Servlet.service() for servlet appServlet threw exception: org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 Unauthorized: [no body]
at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:105) [spring-web-5.3.6.jar:5.3.6]


@RequestMapping(value = “/login/oauth2/code/kakao.do”)
public ModelAndView kakaoSocialLogin(@RequestParam(value = “code”) String authCode, HttpServletRequest request,
HttpServletResponse response, RedirectAttributes rs) throws Exception {
ModelAndView mnv = new ModelAndView();

  RestTemplate restTemplate = new RestTemplate();
  KakaoRequest kakaoOAuthRequestParam = new KakaoRequest();
  kakaoOAuthRequestParam.setClientId(getRegistrationVariable("kakao", "client-id"));
  kakaoOAuthRequestParam.setClientSecret(getRegistrationVariable("kakao", "client-secret"));
  kakaoOAuthRequestParam.setCode(authCode);
  kakaoOAuthRequestParam.setRedirectUri(getRegistrationVariable("kakao", "redirect-uri"));
  kakaoOAuthRequestParam.setGrantType("authorization_code");

  ResponseEntity<KakaoResponse> resultEntity = restTemplate.postForEntity(
  		getRegistrationVariable("kakao", "token-uri"), kakaoOAuthRequestParam, KakaoResponse.class);

String jwtToken = resultEntity.getBody().getId_token();
Map<String, String> idTokenRequest = new HashMap<>();
idTokenRequest.put(“id_token”, jwtToken);
ResponseEntity resultEntity2 = restTemplate
.postForEntity(“https://kauth.kakao.com/oauth/token”, idTokenRequest, KakaoInfResponse.class);

  SocialUser socialUser = resultEntity2.getBody().toSocialUser();

  TokenProvider tokenProvider = new TokenProvider();
  String oauthAccessToken = tokenProvider.generateOAuthAccessToken(response, jwtToken);
  Map<String, String> memberMap = new HashMap<>();
  String id = socialUser.getId();
  String userKey = crypt.getEncrypt(socialUser.getId());
  String email = resultEntity2.getBody().getEmail();
  memberMap.put("JOIN_TYPE", "K");
  memberMap.put("EMAIL", email);
  memberMap.put("USER_KEY", userKey);

  List userInfoList = oAuth2Service.selectUserInfo(memberMap);

  if (userInfoList == null || userInfoList.isEmpty()) {
  	CookieUtil.setCookie("SOCIAL_JOIN_TYPE", "K", request, response);
  	CookieUtil.setCookie("EMAIL", email, request, response);
  	CookieUtil.setCookie("ID", id, request, response);
  	CookieUtil.setCookie("SOCIAL_USER_KEY", userKey, request, response);
  	mnv.setViewName("redirect:/auth/joinAuth.do");
  } else {
  	Map<String, String> user = (Map) userInfoList.get(0);
  	CookieUtil.setCookie("USER_KEY", user.get("USER_KEY"), request, response);
  	mnv.setViewName("redirect:/auth/socialLoginProc.do");
  }
  logger.info("20240220_kakao_login_test_end===========================");
  return mnv;

}

  • 설정된 properties정보

spring.security.oauth2.client.registration.kakao.redirect-uri=http://localhost:18080/portal/login/oauth2/code/kakao.do
spring.security.oauth2.client.registration.kakao.authorization-uri=카카오계정
spring.security.oauth2.client.registration.kakao.token-uri=https://kauth.kakao.com/oauth/token


디버그 해본 결과 오류가 발생하는 곳

ResponseEntity resultEntity = restTemplate.postForEntity(getRegistrationVariable(“kakao”, “token-uri”), kakaoOAuthRequestParam, KakaoResponse.class);


호출이 잘 안되고 있나해서 curl 명령어로 확인했습니다.
(code는 오류 로그에서 가져와서 확인했습니다.)

  • 사용한 curl 명령어

$ curl -v -X POST https://kauth.kakao.com/oauth/token
-H “Content-Type: application/x-www-form-urlencoded”
-d ‘grant_type=authorization_code’
-d ‘code=${정보보호}’
-d ‘client_id=${정보보호}’
-d ‘client_secret=${정보보호}’
-d ‘redirect_uri=http://localhost:18080/portal/login/oauth2/code/kakao.do

  • 결과

Note: Unnecessary use of -X or --request, POST is already inferred.

  • processing: https://kauth.kakao.com/oauth/token
    % Total % Received % Xferd Average Speed Time Time Time Current
    Dload Upload Total Spent Left Speed
    0 0 0 0 0 0 0 0 --:–:-- --:–:-- --:–:-- 0* Trying 27.0.237.15:443…
  • Connected to kauth.kakao.com (27.0.237.15) port 443
  • schannel: disabled automatic use of client certificate
  • using HTTP/1.x

POST /oauth/token HTTP/1.1
Host: kauth.kakao.com
User-Agent: curl/8.2.1
Accept: /
Content-Type: application/x-www-form-urlencoded
Content-Length: 281

} [281 bytes data]

  • schannel: remote party requests renegotiation
  • schannel: renegotiating SSL/TLS connection
  • schannel: SSL/TLS connection renegotiated
  • schannel: remote party requests renegotiation
  • schannel: renegotiating SSL/TLS connection
  • schannel: SSL/TLS connection renegotiated
    < HTTP/1.1 200 OK
    < Date: Thu, 22 Feb 2024 00:27:10 GMT
    < Content-Type: application/json;charset=utf-8
    < Transfer-Encoding: chunked
    < Connection: keep-alive
    < Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    < Pragma: no-cache
    < Expires: 0
    < X-XSS-Protection: 1; mode=block
    < X-Frame-Options: DENY
    < X-Content-Type-Options: nosniff
    < Kakao: Talk
    < Access-Control-Allow-Origin: *
    < Access-Control-Allow-Methods: GET, POST, OPTIONS
    < Access-Control-Allow-Headers: Authorization, KA, Origin, X-Requested-With, Content-Type, Accept
    <
    { [279 bytes data]
    100 543 0 262 100 281 758 813 --:–:-- --:–:-- --:–:-- 1583{“access_token”:“${정보보호}”,“token_type”:“bearer”,“refresh_token”:“${정보보호}”,“expires_in”:21599,“scope”:“account_email”,“refresh_token_expires_in”:5183999}
  • Connection #0 to host kauth.kakao.com left intact

curl 명령어에서는 401이 아니라 HTTP/1.1 200 OK으로 확인했습니다.
client-id, client-secret, code는 문제가 없다고 생각되어 글을 올리게 됐습니다.


다른 분들의 게시글을 참고하여 시도한 방법

  1. header 정보 추가

@RequestMapping(value = “/login/oauth2/code/kakao.do”)
public ModelAndView kakaoSocialLogin(@RequestParam(value = “code”) String authCode, HttpServletRequest request,
HttpServletResponse response, RedirectAttributes rs) throws Exception {
logger.info(“20240220_kakao_login_test_start===========================”);

  ModelAndView mnv = new ModelAndView();
  String credentials = getRegistrationVariable("kakao", "client-id") + ":" + getRegistrationVariable("kakao", "client-secret");
  String base64Credentials = Base64.getEncoder().encodeToString(credentials.getBytes());

  // header Setting
  HttpHeaders headers = new HttpHeaders();
  headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
  headers.add("Authorization", "Basic " + base64Credentials);
  headers.add("Accept", "application/json");   
  
  RestTemplate restTemplate = new RestTemplate();
  restTemplate.setMessageConverters(getMessageConverters());
  
  MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.add("grant_type", "authorization_code");
    params.add("client_id", getRegistrationVariable("kakao", "client-id"));
    params.add("redirect_uri", getRegistrationVariable("kakao", "redirect-uri"));
    params.add("code", authCode);   

  HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers);

오류 로그

10:05:38,183 DEBUG [org.springframework.web.client.RestTemplate] (http-/0.0.0.0:18080-2) Response 401 UNAUTHORIZED

10:05:40,845 DEBUG [org.springframework.web.servlet.DispatcherServlet] (http-/0.0.0.0:18080-2) Failed to complete request: org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 Unauthorized: [no body]

10:05:40,855 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/portal].[appServlet]] (http-/0.0.0.0:18080-2) JBWEB000236: Servlet.service() for servlet appServlet threw exception: org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 Unauthorized: [no body]

  • 오류 로그가 동일했습니다.
  1. IP 허용 주소 추가
  • 오류 로그가 동일했습니다.

안녕하세요.
Client Secret 설정되어 있으나 전달되지 않아 KOE010 오류가 발생하였습니다.

KOE010 오류는 아래 FAQ를 참고하시어 조치 부탁드립니다.

KOE010 (Bad client credentials) 에러가 발생할 때