ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 트위터 소셜로그인 API 2.0 연동하기
    스프링(Spring) 2023. 9. 20. 22:57

     

    트위터 API가 최근 2.0으로 바뀌었다.

    그래서인지는 몰라도 API를 이용하는 것이 유료였고 방법도 구글이나 카카오 로그인과는 조금 다른 듯 했다. 검색해보니 한국어로 정리된 내용들은 대부분 과거 버전의 것이었고 그나마 있는 것들도 뭔가 내가 사용하는 것과는 잘 맞지 않았다. 영어로 검색했을 때 조차도 다른 소셜 로그인 대비 자료가 많지 않았다. 그래서 직접 영문 공식문서를 보고 따라하며 정리해보기로 마음먹었다.(뭐든 공식문서가 기준이 되는 것이 좋으니 더 좋은 시도라 생각한다) 수요가 적을 것 같지만 그래도 연동하는 누군가에게도 도움이 되었으면 좋겠다.

     

    [목표]

    목표로 하는 것은 유저가 "트위터 계정에 로그인하여 액세스 토큰을 발급받고 해당 토큰을 이용해 유저의 기본정보를 요청하여 받아오기"이다. 기본 스펙은 자바와 스프링을 사용한다.

     

    [Authentication flow]

    먼저 트위터 공식문서를 보면 oauth의 인증 흐름은 아래와 같다.

    Twitter oauth 2.0 flow from refering to "https://developer.twitter.com/en/docs/authentication/oauth-2-0/authorization-code"

    흐름과 아래 링크를 참고하여 내가 해야할 것은 "Prepare Authorization URL"에 따라 인가할 URL 준비 -> 유저가 해당 URL로 접속 -> 트위터 계정 로그인 -> 내가 설정한 Redirect_URI로 auth_code와 함께 되돌아오기 -> auth_code를 가지고 액세스 토큰 요청 -> 반환된 액세스 토큰을 가지고 https://api.twitter.com/2/users/me로 요청하여 유저 기본 정보 가져오기

     

    참조: https://developer.twitter.com/en/docs/authentication/oauth-2-0/user-access-token

    https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me


    cf) [Authentication Type]

    여기서 공식 문서에 따르면 Authentication(인증)의 방식에도 App-only Authentication와 USER Authentication이 있다.

     

    아래 링크를 참조하여 얻은 결론을 요약하자면,

    App-only Authentication: 앱과 관련해서 인증하므로 어떤 유저든 상관없음 보통 유저 로그인이 필요하지 않은 정보를 원할 경우

    ex) 인터넷에서 찾을 수 있는 사전 정보 가져오기

    USER Authentication: 유저와 관련해서 인증함, 특정 유저에게 속한 정보가 필요한 경우이며 보통 유저 로그인이 필요

    ex) 특정 유저의 개인 일정 가져오기

     

    나의 경우 특정 유저의 개인 정보가 필요하므로 User Auth라고 판단했다. 

    참조: https://developer.twitter.com/en/docs/authentication/oauth-2-0,

    https://stackoverflow.com/questions/27462106/difference-between-user-and-app-only-auth


    주의할 것은 트위터 oauth API의 경우 CORS를 지원하지 않는다는 것이다. 예를 들자면 로그인 버튼을 만들었는데 로그인 버튼에 직접 Authorization URL을 삽입할 수 없다는 것이다.

    참조: https://stackoverflow.com/questions/35879943/twitter-api-authorization-fails-cors-preflight-in-browser, https://twittercommunity.com/t/cross-origin-resource-sharing-cors-issue-with-twitter-api/194256, https://twittercommunity.com/t/cors-error-in-oauth2-token/163898/17

     

    이를 해결하고자 브라우저에서는 버튼을 누르게 하고, 해당 버튼을 누르면 나의 서버로 ajax요청을 보내도록 설정한다. 그런 다음 서버에서 직접 server to server 로 요청을 보내기로 했다. 자바에서는 보통 이를 위해 Spring의 RestTemplate이나 WebClient를 사용하는 듯 했다.

    참조: https://backtony.github.io/spring/2021-07-12-spring-basic-8/, https://doinge-coding.tistory.com/entry/spring-webClient%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95

    (나의 경우 RestTemplate을 사용해보았다.)

     

    그럼, 본격적으로 트위터 API를 연동하기 전에! 이를 위해 Twitter Developer site에 회원가입이 필요했다.

     

    [트위터 개발자 페이지 회원 가입 과정]

    과거에는 어디에 어떤 용도로 사용할 것인지 자세히 기재하고 이에 대한 승인을 받아야 했다고 한다. 해외에서 검토하다보니 그 과정이 며칠씩 걸리고는 했다는데, 내가 테스트 해본 결과 용도와 사용처를 자세히 기입하기는 했지만 별도의 검토 없이 우선 가입이 완료되었다. 이는 아래와 같다.

     

    트위터 개발자 페이지 회원 가입

    회원 가입을 완료하면 트위터 페이지로 리디렉션된다. 다시 트위터 개발자 페이지를 검색해 접속 후 로그인을 한다.

    개발자 페이지 계정 가입 후 접속할 곳
    트위터의 API는 유료이다. 테스트를 위해 무료를 사용해보자.
    어떤 사이트에 어떤 용도로 사용할 것인지 제출하는 란

    구체적으로 용도와 예시까지 영어로 작성 후 모든 것에 동의하고 제출하면 Delveloper Portal에 들어갈 수 있다. 그곳에서 필요한 모든 Token이나 Key값들을 생성할 수 있다.

     

    [실제 API 연동 과정]

    이제 유저가 클릭을 할 경우 auth_code를 받기 위한 AuthorizationURL을 전달해주고 그곳으로 이동시켜보도록 하자.

    // 유저의 클릭으로 작동할 함수
    function getTwitAuthCodeUrl() {
    	$.ajax({
    		type: 'GET',	
            url: "/twitter/auth-code-url",
            success: function (authCodeUrl) {
    		location.href = authCodeUrl;				          
            },
        });		   
    }
    // 요청에 따라 반환할 주소
    String url = "https://twitter.com/i/oauth2/authorize?response_type=code&client_id=" + ClientID
                    + "&redirect_uri=" + auth_code와 함께 redirect되고 싶은 URL 
                    + "&scope=tweet.read%20users.read&state=state&code_challenge=challenge&code_challenge_method=plain"
                    
                    //refreshToken이 필요할 경우, scope부분에 "%20offline.access"도 추가해주도록 하자.
                    // 추가되지 않으면 accessToken의 유효시간은 2시간이다.

    여기서 Client ID는 가입한 트위터 개발자 계정 내 Developer Portal에서 찾아볼 수 있다. 그 예시는 다음과 같다.

    이렇게 받아온 URL로 접속하면 아래와 같이 트위터 로그인 창이 나타난다.

    트위터 로그인 창

    cf) 실은 위의 창이 바로 나타나는 것은 아니고 트위터 계정으로 로그인 하는 창이 나타난다. 여기서 트위터 계정에 로그인 하면 그 다음 위의 창이 나타나야하겠지만 트위터는 그러지 않고 곧바로 유저를 트위터 메인 사이트로 보내버린다. 해당 부분은 현재 문서를 보아도 내가 개인적으로 조절할 수 없는 부분으로 판단돼 우선은 불편을 감수하고 사용한다. 트위터 계정 로그인 후 뒤로가기를 했다가 다시 트위터 로그인을 클릭해서 위의 창으로 들어가자.

    여기서 Cancel을 클릭하면 http://localhost?error=access_denied&state=state와 같이

    ”error=access_denied&state=state”라는 파라미터를 반환받는다.

    Authorize App을 클릭하면 아래와 같이 auth_code가 담긴 파라미터가 포함된 URL을 받환받는다.

    "http://localhost?state=state&code=auth_code"

    cf) state: A random string you provide to verify against CSRF attacks.  The length of this string can be up to 500 characters. 즉, CSRF 공격에 대비한 임의의 문자열이라는 말이다.

    그렇게 auth_code와 함께 위에서 설정한 redirect_uri로 돌아오게 된다. 이제 이것으로 access token을 요청해야하는데 언급했다시피 CORS는 지원하지 않는다. 고로 redirect_uri에 해당하는 컨트롤러를 만들고 해당 경로에서 auth_code가 파라미터에 포함되어있다면 access token을 요청하는 것으로 구현한다. 아래와 같을 것이다.

     

    @GetMapping("/path-you-set-for-the-redirection")
        public String getUserTwitterInfo(@RequestParam(value = "code", required = false, defaultValue = "") String authCode)
        throws Exception {
            
            if(!authCode.equals("")) {
                
                // 액세스 토큰 요청     
    
               // 액세스 토큰을 가지고 유저 개인정보 요청
            }
            return 유저의 트위터 개인정보
        }

    공식 문서에 따르면 액세스 토큰을 요청할 때 다음과 같은 항목이 필요하다고 한다.

    curl --location --request POST 'https://api.twitter.com/2/oauth2/token' \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --header 'Authorization: Basic V1ROclFTMTRiVWhwTWw4M2FVNWFkVGQyTldNNk1UcGphUTotUm9LeDN4NThKQThTbTlKSXQyZm1BanEzcTVHWC1icVozdmpKeFNlR3NkbUd0WEViUA=='\
    --data-urlencode 'code=VGNibzFWSWREZm01bjN1N3dicWlNUG1oa2xRRVNNdmVHelJGY2hPWGxNd2dxOjE2MjIxNjA4MjU4MjU6MToxOmFjOjE' \
    --data-urlencode 'grant_type=authorization_code' \
    --data-urlencode 'redirect_uri=https://www.example.com' \
    --data-urlencode 'code_verifier=challenge'

     

    이때 Authorization에 사용되는 Basic타입 토큰은 어떻게 만들 수 있을까? 아래와 같다.

     

    public String requestAccessTokenTest(String authCode) {
    
            String requestUri = "https://api.twitter.com/2/oauth2/token";
            // 관련 메서드 참조 링크
            // https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/HttpHeaders.html
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            headers.setBasicAuth(clientID, clientSecret);
    
            // 필요한 요소 추가하여 토큰 값 받아옴
    
        }

    공식문서에는 다음과 같이 나와있다.

    "To create the basic authorization header you will need to base64 encoding on your Client ID and Client Secret which can be obtained from your App’s “Keys and Tokens” page inside of the developer portal."

    즉, Client ID and Client Secret을 쓰라는 것이다.

    참조: https://developer.twitter.com/en/docs/authentication/oauth-2-0/user-access-token

     

    이렇게 하여 토큰을 받고난 후 유저의 개인정보를 서버에서 곧바로 요청할 것이다. 이때 주의할 것은 문서와 다르게 한 유저당 24시간 이내 25번까지 요청할 수 있다는 것이다. (Developer Portal내에서 확인할 수 있었다.) 요청 횟수가 넘어갈 경우 다음과 같이 에러가 발생하여 에러처리가 필요하다.

    try {
           // 요청 코드        
            } catch (HttpClientErrorException e) {
    	// 요청 회수 넘어갈 시 에러 처리: HttpClientErrorException$TooManyRequests:
        // 429 Too Many Requests: "{"title":"Too Many Requests","detail":"Too Many Requests","type":"about:blank","status":429}"        
            	}

    참조: https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me

     

    횟수를 초과하지 않았다면 성공적으로 유저의 정보를 가져올 것이다! 추가적으로 필요한 정보는 문서를 참조하여 커스텀하면 된다.

     

    [후기]

    API가 2.0으로 개편된지 얼마되지 않아 정보를 찾는 것이 쉽진 않았다.(무료버전인 ChatGPT도 2021년 9월까지의 정보가 기준이라 트위터 2.0에 대한 정보를 물어볼 수도 없었다.) 공식문서를 보아도 명쾌하게 설명되어 있는 부분이 적었고 구버전과 섞여있어 더 찾기 쉽지 않았던 것 같다. 하지만 하나씩 찾아가며 테스트해본 결과 원하는 값을 얻어낼 수 있었다. 역시 무엇이든 꾸준히 들여다보고 시도해보면 할 수 있게 된다! 문제 해결 과정과 결과의 즐거움을 느끼며 앞으로도 성장해나가보도록 하자! :)

     

    '스프링(Spring)' 카테고리의 다른 글

    [스프링] 사용자 IP를 추적하는 방법  (2) 2023.07.22
Designed by Tistory.