python-3.xoauth-2.0google-apiyoutube-analytics-api

Unable to redirect the user to auth_uri while using google flow 2.0


I am working on an application to pull the YouTube metrics using the YouTube analytics API. This is the first time I am working with google auth flow to authenticate my app so that it can silently pull the report.

I am following the below Google published article to work out the same: https://developers.google.com/youtube/reporting/guides/authorization/server-side-web-apps

While following this article I am unable to figure out how to redirect the user to the auth_uri and obtain the auth_code.

Below is the code I have written for the auth flow so far:

API_SERVICE_NAME = 'youtubeAnalytics'
API_VERSION = 'v2'
CLIENT_SECRETS_FILE = 'C:/Users/Tushar/Documents/Serato_Video_Intelligence/client_secret_youtube.json'
def get_service():
  global auth_code
  global auth_uri
  flow = client.flow_from_clientsecrets(
  CLIENT_SECRETS_FILE, 
  scope='https://www.googleapis.com/auth/yt-analytics.readonly',
  redirect_uri = "http://localhost:8080")
  flow.params['access_type'] = 'offline'
  flow.params['include_granted_scopes'] = True
  auth_uri = flow.step1_get_authorize_url()
  credentials = flow.step2_exchange(auth_code)
  http_auth = credentials.authorize(httplib2.Http())
  return build(API_SERVICE_NAME, API_VERSION, http=http_auth)
  
def execute_api_request(client_library_function, **kwargs):
  response = client_library_function(
    **kwargs
  ).execute()
        

if __name__ == '__main__':
  # Disable OAuthlib's HTTPs verification when running locally.
  # *DO NOT* leave this option enabled when running in production.
  #os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'

  youtubeAnalytics = get_service()
  execute_api_request(
      youtubeAnalytics.reports().query,
      ids='channel==XXXXXXXXXXXXXX',
      startDate='2019-04-12',
      endDate='2019-08-13',
      filters= 'video==XXXXXXXXXXXXXX',
      metrics='audienceWatchRatio,relativeRetentionPerformance',
      dimensions='elapsedVideoTimeRatio'
      
  )

When I run this code I get an error

  File "C:\Users\Tushar\Documents\Serato_Video_Intelligence\youtube_auth_testing.py", line 65, in <module>
    youtubeAnalytics = get_service()
  File "C:\Users\Tushar\Documents\Serato_Video_Intelligence\youtube_auth_testing.py", line 40, in get_service
    credentials = flow.step2_exchange(auth_code)
NameError: name 'auth_code' is not defined

I have gone through the articles on Stack Overflow and Google but have been unable to figure out what to do. I certainly know there are couple of steps that I am missing but I am unable to resolve it.


Solution

  • If my understanding is correct, how about this modification?

    Authorization flow:

    When the access token is retrieved by the authorization process of OAuth2, at first, it is required to authorize the scopes by own browser. When the scopes are authorize, the authorization code can be retrieved. Using this code, the refresh token and access token can be retrieved.

    Patten 1:

    When your current script is modified, it becomes as follows. In this modification, please modify get_service().

    Modified script:

    import httplib2
    from apiclient.discovery import build
    from oauth2client import client
    
    def get_service():
        flow = client.flow_from_clientsecrets(
            CLIENT_SECRETS_FILE,
            scope='https://www.googleapis.com/auth/yt-analytics.readonly',
            redirect_uri='urn:ietf:wg:oauth:2.0:oob')
        flow.params['access_type'] = 'offline'
        auth_uri = flow.step1_get_authorize_url()
        print('Please go to this URL: {}'.format(auth_uri))
        auth_code = input('Enter the authorization code: ')
        credentials = flow.step2_exchange(auth_code)
        http_auth = credentials.authorize(httplib2.Http())
        return build(API_SERVICE_NAME, API_VERSION, http=http_auth)
    

    Or (This is from the sample script of Reports: Query.)

    def get_service():
        flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES)
        credentials = flow.run_console()
        return build(API_SERVICE_NAME, API_VERSION, credentials = credentials)
    

    Patten 2:

    In this pattern, when the script is run, the browser is automatically opened, and when the scopes are manually authorized, the authorization code is automatically retrieved.

    Modified script:

    import httplib2
    import os
    from apiclient.discovery import build
    from oauth2client import client
    from oauth2client import tools
    from oauth2client.file import Storage
    
    def get_service():
        SCOPES = 'https://www.googleapis.com/auth/yt-analytics.readonly'
        credential_path = os.path.join("./", 'tokens.json')
        store = Storage(credential_path)
        credentials = store.get()
        if not credentials or credentials.invalid:
            flow = client.flow_from_clientsecrets(CLIENT_SECRETS_FILE, SCOPES)
            credentials = tools.run_flow(flow, store)
        http = credentials.authorize(httplib2.Http())
        return build(API_SERVICE_NAME, API_VERSION, http=http_auth)
    

    Patten 3:

    Recently, google_auth_oauthlib is used for the authorization process. For example, you can see it at here. When this is reflected to your script, get_service() becomes as follows. Also you can see this flow at here.

    Modified script:

    import pickle
    import os.path
    from googleapiclient.discovery import build
    from google_auth_oauthlib.flow import InstalledAppFlow
    from google.auth.transport.requests import Request
    
    def get_service():
        SCOPES = ['https://www.googleapis.com/auth/yt-analytics.readonly']
        TOKEN_FILE = 'token.pickle'
        creds = None
        if os.path.exists(TOKEN_FILE):
            with open(TOKEN_FILE, 'rb') as token:
                creds = pickle.load(token)
        if not creds or not creds.valid:
            if creds and creds.expired and creds.refresh_token:
                creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file(
                    CLIENT_SECRETS_FILE, SCOPES)
                creds = flow.run_local_server()
            with open(TOKEN_FILE, 'wb') as token:
                pickle.dump(creds, token)
        return build(API_SERVICE_NAME, API_VERSION, credentials=creds)
    

    Note:

    References:

    If I misunderstood your question and this was not the direction you want, I apologize.