본문으로 바로가기
728x90
반응형

 

Google OAuth Token을 얻는 과정이 매번 번거로운데
어떻게 하면 편하게 얻을 수 있을까?

목차


    개요 

    최근 Google OAuth Login 기능을 구현하면서 상당히 번거로운 점을 체감했다.

     

    매번 url을 클릭해서 브라우저를 열고 계정을 선택해서 나온 callback url에서 code를 따로 추출하고 url decoding을 한 뒤 이를 다시 access_token으로 가져오기 위한 코드를 사용한다는 점이 그러했다.

     

    어떻게 하면 이렇게 번거로운 과정을 단축하여 편하게 id_token과 access_token을 얻을 수 있을지 고민했는데 Google 개발 문서를 뒤적거리고 조금의 아이디어를 보태서 해결할 수 있었다.

     

    그러한 배경으로 앞서 이야기한 번거로운 과정을 어떻게 해결했는지 기록해 보고자 한다.

     

     

    1.  번거로운 부분

    Google OAuth에서 제공하는 access_token을 얻기까지는 다음 단계로 나눌 수 있다.

    1. url 클릭 (OAuth 진행을 위한 브라우저 오픈)
    2. 계정 선택
    3. callback 확인 후  url의 query param 중 code 부분의 value 추출
    4. 추출한 value를 url decode 시키기
    5. 추출한 code를 통해 access_token 얻기

    (자세한 부분은 링크 참고)

     

    한 번이면 괜찮다. 그리고 이렇게 얻은 token은 1시간 동안 유효하니 1시간 동안은 재사용이 가능하다. 그러나 이러한 사실에도 불구하고 그냥 매번 위와 같은 과정을 진행하여 token을 얻곤 했다.

     

    이 과정을 매번 반복하다 보니 google oauth의 access token을 얻는 과정이 너무 수동적이며 번거로웠다.

     

     

    2. Google OAuth에 계정을 미리 선택하기

    이렇게 번거로운 과정을 해결하기에 앞서 필요한 부분이 무엇인지 생각했을 때는 아무래도 OAuth Login을 진행할 계정을 직접 선택하는 부분이었다. 

     

    이 부분을 손수 선택하는 것이 아닌 code 레벨에서 제어할 수 있는 방법에 대해 조사하게 됐는데 이 문서에서 힌트를 얻을 수 있었다.

     

    OAuth의 Authorization Code를 얻기 위한 url을 생성할 때 "login_hint"라는 key에 특정 email을 지정하면 해당 email로 바로 authorization code가 포함된 callback url을 얻을 수 있게 된다.

     

    이 사실을 코드로 옮기면 다음과 같이 작성할 수 있다 

    from urllib import parse
    url = "https://accounts.google.com/o/oauth2/v2/auth"
    params = {
        "scope": 'https://www.googleapis.com/auth/userinfo.profile',
        "access_type": "offline",
        "include_granted_scopes": "true",
        "redirect_uri": "http://localhost:3000/api/auth/callback/google",
        "client_id": client_id,
        "response_type": "code",
        "login_hint": "YOUR EMAIL"
    }
    url = url + "?" + parse.urlencode(params)

     

     

    3.  Redirect URI 문제 

    앞서 언급한 방식에서 얻은 url을 python의 requests로 get 요청을 하면 바로 callback url을 얻어서 이후 과정을 진행하면 될 것 같았지만 사실은 그러지 못했다.

     

    아무래도 IDP로부터 다시 Request를 쏘는 형태라 그런 듯했다. Redirect URI를 컨트롤할 수 있는 방법을 조사해 봤지만 쉽게 해결법을 찾진 못했다.

     

    그래서 단순하게 localhost:3000번으로 가벼운 http server를 띄워서 해결했다. Flask나 Django, FastAPI를 쓸 수 있겠지만 사용하려고 보니 배보다 배꼽이 더 큰 상황이다.

     

    적정한 방법으로 Python에서 built-in으로 제공하는 BaseHTTPRequestHandler를 사용했다. 이에 대한 기본 예제는 다음과 같다.

    from http.server import BaseHTTPRequestHandler, HTTPServer
    
    class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
    
        def do_GET(self):
            self.send_response(200)
            self.send_header('Content-type', 'text/plain')
            self.end_headers()
    
    httpd = HTTPServer(('0.0.0.0', 3000), SimpleHTTPRequestHandler)
    print(f'Server running on port:{3000}')
    httpd.serve_forever()

     

     

    4.  Google Oauth Access Token의 EndPoint

    Authorization Code까지 얻는 걸 자동으로 만들었으니 Access Token은 선택사항이라 조사를 할지 말지 고민했는데 이왕 하는 김에 이것까지 조사했다. 

     

    Google OAuth Access Token까지 얻는 걸 자동화하려면 AccessToken을 획득할 수 있는 EndPoint가 필요했는데 결국 다음과 같은 코드를 사용하여 해결할 수 있었다.

    token_uri = "https://oauth2.googleapis.com/token"
    r = requests.post(token_uri, data={
        "client_id": client_id,
        "client_secret": client_secret,
        'redirect_uri': 'http://localhost:3000/api/auth/callback/google',
        "grant_type": "authorization_code",
        "code": parse.unquote(code)
    })

    data에서 code라고 되어있는 부분은 Authorization Code이며 parse.unquote()를 통해 url decoding을 적용했다.

     

    5. 전체 코드

    전체 코드는 다음과 같다.

     

    필요한 부분들과 수정이 필요한 부분들은 상황에 맞게 수정해서 사용하도록 하자.

     

     

    마치며

    "개요"와 본문에서 언급했듯이 단순히 개발 중에 편의상 Google OAuth Token을 획득하는 과정이 번거롭기 때문에 이러한 시도를 해봤다.

     

    시도를 해보는 중에 테스트 코드를 작성할 때 이러한 아이디어를 도입해 Google OAuth Token을 얻는 것을 검증해 보는 것도 괜찮겠다 싶었지만 현재로서는 한계점이 존재하는 듯싶다. 본문에서는 언급이 안되어있지만 본문에 적은 내용을 시도하기 전에는 Google 계정으로 로그인된 브라우저가 있어야 한다는 점이 그러하다.

     

    OAuth Login 과정은 그 플로우의 복잡함이 있는지라 모든 부분을 자동화를 통해 구현하기엔 스스로의 아이디어가 조금 부족한데 시간이 지나고 나서 다시 시도해보자.

     

     

     


     

    728x90
    반응형