3
votes

I am trying to access documents stored in Google Drive for my organization via a python script.

Here is what I have done:

  1. Created a new service account and chose "Enable G Suite Domain-wide Delegation"
  2. I then went into GSuite admin for my account and via Security -> Advanced Settings -> Manage API client access I added the client id generated for my new service account and these permissions as detailed here: enter image description here

I then have the following python method to build a service to access gsuite documents:

def get_service(api_name, api_version, scopes, key_file_location):
    """Get a service that communicates to a Google API.

    Args:
        api_name: The name of the api to connect to.
        api_version: The api version to connect to.
        scopes: A list auth scopes to authorize for the application.
        key_file_location: The path to a valid service account JSON key file.

    Returns:
        A service that is connected to the specified API.
    """

    # credentials = ServiceAccountCredentials.from_json_keyfile_name(
    #         key_file_location, scopes=scopes)
    credentials = service_account.Credentials.from_service_account_file(
        key_file_location, scopes=scopes)

    delegated_credentials = credentials.with_subject('[email protected]')

    # Build the service object.
    service = build(api_name, api_version, credentials=delegated_credentials)

    return service

When I try to access a spreadsheet I get the following error:

('unauthorized_client: Client is unauthorized to retrieve access tokens using this method.', u'{\n "error": "unauthorized_client",\n "error_description": "Client is unauthorized to retrieve access tokens using this method."\n}')

The spreadsheet has permissions anyone in organization can view.

I tried also manually adding the Service Account email address to the spreadsheet permissions and doing that allows me to access the document without using the delegated credentials, but I'd like to avoid having to add the email address to each spreadsheet I want to access.

How can I programmatically view all Google sheets viewable by members of my organization using Python?

Thanks.

1
Are you using the right call to get delegated credentials, e.g. delegated_credentials = credentials.create_delegated('[email protected]')AChampion
could you please provide the full stack trace and line numbers?maininformer
You may want to look at this post: stackoverflow.com/questions/49374112/…AChampion

1 Answers

1
votes

Thanks for the pointers @AChampion. The problem turned out to be requesting scope https://www.googleapis.com/auth/spreadsheets.readonly when I had only authorized scope https://www.googleapis.com/auth/spreadsheets. I thought spreadsheets would be a superset of spreadsheets.readonly but that doesn't seem to be the case.

My get_service code:

def get_service(api_name, api_version, scopes, key_file_location):
    """Get a service that communicates to a Google API.

    Args:
        api_name: The name of the api to connect to.
        api_version: The api version to connect to.
        scopes: A list auth scopes to authorize for the application.
        key_file_location: The path to a valid service account JSON key file.

    Returns:
        A service that is connected to the specified API.
    """

    credentials = ServiceAccountCredentials.from_json_keyfile_name(
            key_file_location, scopes=scopes)
    # credentials = service_account.Credentials.from_service_account_file(
    #     key_file_location, scopes=scopes)

    delegated_credentials = credentials.create_delegated('[email protected]')  
    delegated_http = delegated_credentials.authorize(Http())

    # Build the service object.
    service = build(api_name, api_version, http=delegated_http)

    return service

And my call to get_service:

scope = 'https://www.googleapis.com/auth/spreadsheets'
key_file_location = '/path/to/keyfile.json'
service = get_service(
            api_name='sheets',
            api_version='v4',
            scopes=[scope],
            key_file_location=key_file_location)