Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to instantiate a Google API service using google-auth?

I'm trying to populate a Google sheet from a Python program using its API, using the quickstart guide (https://developers.google.com/sheets/api/quickstart/python) as a starting point:

from __future__ import print_function
from apiclient.discovery import build
from httplib2 import Http
from oauth2client import file as oauth_file, client, tools

# Setup the Sheets API
SCOPES = 'https://www.googleapis.com/auth/spreadsheets.readonly'
store = oauth_file.Storage('token.json')
creds = store.get()
if not creds or creds.invalid:
    flow = client.flow_from_clientsecrets('credentials.json', SCOPES)
    creds = tools.run_flow(flow, store)
service = build('sheets', 'v4', http=creds.authorize(Http()))

However, I was only able to get this to work using the flow_from_clientsecrets() method, which (by default) opens an authentication page in the browser. This does not seem suitable for something I'd like to run periodically on a production server.

In any case, according to https://pypi.org/project/oauth2client/, oauth2client is deprecated and developers are recommended to use google-auth instead.

Therefore, I tried to adapt this example as follows (following https://google-auth.readthedocs.io/en/latest/user-guide.html#service-account-private-key-files):

from google.oauth2 import service_account

credentials = service_account.Credentials.from_service_account_file(
    'google_client_secret.json')

Where 'google_client_secret.json' is the JSON file downloaded from the API console, which looks like this (scrambled and pretty-printed):

{
  "installed": {
    "client_id": "33e.apps.googleusercontent.com",
    "project_id": "nps-survey-1532981793379",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://accounts.google.com/o/oauth2/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_secret": "foobar",
    "redirect_uris": [
      "urn:ietf:wg:oauth:2.0:oob",
      "http://localhost"
    ]
  }
}

When I run the script, however, I get the following error:

(lucy-web-CVxkrCFK) bash-3.2$ python nps.py
Traceback (most recent call last):
  File "nps.py", line 25, in <module>
    'google_client_secret.json')
  File "/Users/kurtpeek/.local/share/virtualenvs/lucy-web-CVxkrCFK/lib/python3.7/site-packages/google/oauth2/service_account.py", line 209, in from_service_account_file
    filename, require=['client_email', 'token_uri'])
  File "/Users/kurtpeek/.local/share/virtualenvs/lucy-web-CVxkrCFK/lib/python3.7/site-packages/google/auth/_service_account_info.py", line 73, in from_filename
    return data, from_dict(data, require=require)
  File "/Users/kurtpeek/.local/share/virtualenvs/lucy-web-CVxkrCFK/lib/python3.7/site-packages/google/auth/_service_account_info.py", line 51, in from_dict
    'fields {}.'.format(', '.join(missing)))
ValueError: Service account info was not in the expected format, missing fields token_uri, client_email.

From dropping into the debugger, I noticed that the problem is basically that the dictionary passed into the from_dict() method is the entire dictionary in the google_client_secret.json file, which has only one key, "installed". What the from_dict() method seems to be 'looking for' is the sub-dictionary, as this contains a token_uri key, although even this doesn't contain the required client_email key.

What I'm suspecting is that I've created the wrong type of OAuth2 client for my use case, because the JSON containing the client secrets isn't in the expected format. Any ideas how I could fix this?

like image 300
Kurt Peek Avatar asked Jul 30 '18 23:07

Kurt Peek


People also ask

How do I authenticate Google services API?

With a user account, you can authenticate to Google APIs and services in the following ways: Use the gcloud CLI to set up Application Default Credentials (ADC). Use the gcloud CLI to generate access tokens. Use your user credentials to impersonate a service account.

How do I authenticate Gmail API?

All requests to the Gmail API must be authorized by an authenticated user. Gmail uses the OAuth 2.0 protocol for authenticating a Google account and authorizing access to user data. You can also use Google Sign-in to provide a "sign-in with Google" authentication method for your app.


1 Answers

From https://developers.google.com/api-client-library/python/guide/aaa_oauth, there are three types of client IDs:

  1. Web application client IDs
  2. Installed application client IDs
  3. Service Account client IDs

My use case is a Service Account, and upon creating one and choosing the Furnish a new private key option, I found that I obtained a JSON file which does match the expected format. (The one I had was for an installed application).

like image 88
Kurt Peek Avatar answered Sep 30 '22 11:09

Kurt Peek