카카오 로그인 오류 [Security + OAuth2]

wish17·2023년 5월 10일
0

오류정리

목록 보기
4/7
post-thumbnail

오류

카카오 로그인 기능을 추가하다 발생한 오류다.

구글 연동에 성공하고 카카오 연동을 시도했다.
구글의 경우는 provider가 security 설정에 내장되어 있어 따로 추가할 필요 없었지만 카카오 같은 우리나라에서만 주로 사용되는 경우에는 provider를 직접 설정해야 한다.

// 오류가 발생한 yml 설정파일
logging:
  level:
    org:
      springframework:
        web: debug
    org.springframework.web.servlet: debug
    org.hibernate.type.descriptor.sql.BasicBinder: trace

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/edusync?serverTimezone=Asia/Seoul
    username: root
    password: ${localDBPassword}
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
    log-request-details: true
  jpa:
    defer-datasource-initialization: true
    hibernate.ddl-auto: create-drop
    open-in-view: false
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        default_batch_fetch_size: 100
        highlight_sql: true
        color-codes: true
        type:
          descriptor:
            sql: trace
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: 90423476142-0p4j9mmj540elt7qgg5udb7hsiog7hi2.apps.googleusercontent.com
            clientSecret: ${GoogleClientSecret}
            scope:
              - email
              - profile
          kakao:
            client-id: ${KakaoClientId}
            client-secret: ${KakaoClientSecret}
            authorization-grant-type: authorization_code
            client-name: Kakao
            provider: kakao
            redirect-uri: "http://localhost:8080/login/oauth2/code/kakao"
            scope:
              - profile_nickname
              - profile_image
              - account_email
        provider:
          kakao:
            authorization-uri: https://kauth.kakao.com/oauth/authorize
            token-uri: https://kauth.kakao.com/oauth/token
            user-info-uri: https://kapi.kakao.com/v2/user/me
            user-name-attribute: id
mail:
  address:
    admin: admin@gmail.com
jwt:
  key: ${JWT_SECRET_KEY}
  access-token-expiration-minutes: 30
  refresh-token-expiration-minutes: 420

server:
  port: 8080

위와 같이 설정해 정상적으로 http://localhost:8080/oauth2/authorization/kakao에서 카카오 로그인을 진행할 수 있었다.

하지만 인증 code는 정상적으로 받아오지만 토큰 요청을 보내면 카카오에서 401로 응답했다.


해결 시도

1. 환경변수, 동의항목 확인

Secret key등 환경변수를 확인하고 카카오 developer 동의항목 등 설정사항을 더블체크했지만 문제가 없었다.

2. http 통신 log 남기기

잘못된 설정을 한 부분이 도저히 보이지 않아 아래와 같이 응답 바드와 헤더를 더 자세히 보기 위해 필터를 추가해 봤지만 카카오측에서 특별히 제공해주는 경고문 같은게 없어서 공백만 보일 뿐이였다.

3. 엔드포인트를 이용해 직접 통신 로직 구현

카카오 developers에 인증코드와 토큰을 받기 위한 요청의 형태에 대해 자세히 나와있다. 이를 이용하면 Security 자동구현에 기대지 않고 모든 통신과정을 컨트롤 할 수 있다.

@RestController
@RequestMapping("/oauth2")
@Slf4j
public class OAuth2Controller {

    @Value("${KakaoClientId}")
    private String kakaoClientId;

    @Value("${KakaoClientSecret}")
    private String kakaoClientSecret;

    @ResponseBody
    @GetMapping("/code/kakao")
    public String kakaoCallback(@RequestParam String code) {

        RestTemplate restTemplate = new RestTemplate();

        String codeVerifier = "YOUR_CODE_VERIFIER";
        String codeChallenge = "YOUR_CODE_CHALLENGE";

        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
        parameters.add("grant_type", "authorization_code");
        parameters.add("client_id", kakaoClientId);
        parameters.add("client_secret", kakaoClientSecret); // Add client_secret
        parameters.add("redirect_uri", "http://localhost:8080/oauth2/code/kakao");
        parameters.add("code", code);
        parameters.add("code_verifier", codeVerifier);

        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(parameters, headers);
        String credentials = kakaoClientId + ":" + kakaoClientSecret;
        String encodedCredentials = new String(Base64.getEncoder().encode(credentials.getBytes()));
        headers.add("Authorization", "Basic " + encodedCredentials);

        ResponseEntity<String> response = restTemplate.postForEntity("https://kauth.kakao.com/oauth/token", request, String.class);
        if (response.getStatusCode() == HttpStatus.OK) {
            Gson gson = new Gson();

            Map<String, Object> responseMap = gson.fromJson(response.getBody(), Map.class);
            String accessToken = (String) responseMap.get("access_token");
            log.info("Access Token: {}", accessToken);
            return accessToken;
        } else {
            log.error("Error occurred while fetching access token: {}", response.getStatusCode());
            return "Error";
        }

    }
}

위와 같이 통신과정을 직접 구현했더니 문제 없이 카카오측으로 부터 엑세스 토큰을 받을 수 있었다.

이를 통해 환경변수나 local환경의 문제가 아닌 security 자동구성 과정에서 발생하는 문제임을 알 수 있었다.

여기서 나는 아래 두가지 중 하나를 선택해야 했다.

    1. 위 코드와 같은 방식으로 카카오 develpers 페이지를 참고하여 모든 통신과정 로직을 작성하기
    1. security 자동구현에서 잘못되는 원인을 찾기

우선 1번처럼 어떻게 토큰까지는 받아왔지만 솔직히 쉬운 과정이 아니였다. 그런데 코드의 중복을 줄이려면 결국 카카오 인증 정보를 security context에 저장 해야하는데 외부로직에서 접근하다보니 예상치 못한 변수가 많이 발생할 것으로 예상되었다.

그래서 우선 2번 방법을 통해 해결해보도록 노력하고 최후의 수단으로 1번 방법을 남겨두기로 했다.


문제해결

정말 사소한 문제였다. 요청 method 타입을 지정해주지 않아 발생한 문제였다. 구글 인증과정에서는 security 자동구성에서 타입설정에 상관없이 처리되다 보니 미처 생각하지 못했다.

아래와 같이 설정 한줄 추가하니 정상적으로 동작한다.

          kakao:
            client-id: ${KakaoClientId}
            client-secret: ${KakaoClientSecret}
--------------------------문제였던 부분--------------------------            
            client-authentication-method: POST
--------------------------문제였던 부분--------------------------            
            authorization-grant-type: authorization_code
            client-name: Kakao
            provider: kakao
            redirect-uri: "http://localhost:8080/login/oauth2/code/kakao"
            scope:
              - profile_nickname
              - profile_image
              - account_email
        provider:
          kakao:
            authorization-uri: https://kauth.kakao.com/oauth/authorize
            token-uri: https://kauth.kakao.com/oauth/token
            user-info-uri: https://kapi.kakao.com/v2/user/me
            user-name-attribute: id

이후 SuccessHandler까지 구현 완료해 아래와 같이 정상적으로 인증성공 + 회원 정보 db 저장 + 내 서버에서 토큰 발급 후 전달까지 성공했다.

0개의 댓글