DEV Community

Cover image for Google Spreadsheets OAuth Authentication with Python
Una Scott
Una Scott

Posted on

Google Spreadsheets OAuth Authentication with Python

Introduction

This article shows you how to connect to Google Spreadsheets API using
OAuth with Python. OAuth (Open Authorization) is an open standard
protocol used for token-based authentication and authorization,
allowing third-party applications to access a user's resources without
exposing their credentials. What this means in practical terms is that
you can allow an external application to access your google
spreadsheets without having to share your account name and
password. The way this works is: the external application redirects
you to a Google consent screen which you can approve or refuse. Once
you approve the access request, Google then hands an approval token
to the application which can be used for accessing your spreadsheets.

We are using this method to connect a user's spreadsheets to their
account on Teramine Forms. This way, when a
user fills in a form, the data is uploaded immediately to the
specified google spreadsheet without manual intervention.

Google API Docs are hard to understand

The main motivation for this article is that: in 2024, we had to spend
considerable time wading through Google API docs to implement this
functionality. Part of the reason was that some of the Google API docs
are out of date or just plain wrong, which you discover when
attempting to use them. For example, even authenticating with Google
servers for invoking spreadsheet related functions was a bit of a pain
because the information required comes from various different sources,
as you will see in the article later.

So we hope that this article will ease the process for anyone trying
to connect to a Google spreadsheet to create or update data.

Setup Oauth Consent Screen

The first step is to go to Google Cloud
Console
, login with your account
credentials, and create the OAuth consent screen. Follow the procedure
outlined in this
document

and complete the procedure. Follow through the procedure to create an
OAuth 2.0 Client ID too as shown in the picture below. Finally
download the OAuth Client information as JSON.

Google OAuth Client Configuration

The downloaded file will be named something like
client_secret_<something>.json. Save it in a safe place. We are
going to need it for authentication.

Here is a sample just so you know what it looks like:

{
    "web": {
        "client_id": "577xxxxx.apps.googleusercontent.com",
        "project_id": "teramine-forms",
        "auth_uri": "https://accounts.google.com/o/oauth2/auth",
        "token_uri": "https://oauth2.googleapis.com/token",
        "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
        "client_secret": "xxxxxxxxxxxx",
        "redirect_uris": [
            "https://example.com/oauth/callback/googlesheets/"
        ]
    }
}
Enter fullscreen mode Exit fullscreen mode

The fields client_id and client_secret are specific to you
application, and the redirect_uris will contain values you specified
with you configured the "OAuth Consent Screen". It need not take the
same form as shown above and it can be anything specific to your
application. When a user consents to allowing access to your
application, Google will invoke this URL and pass a code parameter
which is the approval token you application can use to retrieve an
access token.

Congratulations! You have completed the first step in preparing for
Google Spreadsheets Authentication.

Using Google API for Authentication

We will now look into authenticating an application using the
client_secret.json file we just downloaded. For this purpose we will
use the official Google API.

Installing the Official Google API for Python

Begin by installing the official Google API.

pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib
Enter fullscreen mode Exit fullscreen mode

This step assumes that you have setup a Python virtual environment for
you application. If this is not the case, please look at other
documents on the Internet how to setup a Python virtual environment.

Authenticate with Google to retrieve an access token

The next step is to authenticate using the Google API and retrieve an
access token. The access token is what actually allows invocation
of the spreadsheet API. The access token has a short time out period
of a few minutes, so when that expires you need to use a refresh
token
which allows you to refresh the access token.

Creating a Flow object

We begin by importing the following library into the sample code.

import google_auth_oauthlib.flow
Enter fullscreen mode Exit fullscreen mode

Next is to create a flow object as follows. Note that we use the
client_secret.json file we saved in the previous step. It has been
renamed here from client_secret_<something>.json to
client_secret.json for easier access.

You can also see the scopes that we need for the application. You
should have specified these scopes when you configured the Google
OAuth configuration screen.

flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    'client_secret.json',
    scopes=["https://www.googleapis.com/auth/userinfo.profile",
            "https://www.googleapis.com/auth/userinfo.email",
            "https://www.googleapis.com/auth/spreadsheets",
            "https://www.googleapis.com/auth/drive",
            "openid"])
flow.redirect_uri = "https://example.com/oauth/callback/gsheets/"
Enter fullscreen mode Exit fullscreen mode

We are using the userinfo.profile and userinfo.email scopes too,
but you could remove them if not needed.

The spreadsheets scope is required for reading and writing a Google
Spreadsheet.

The drive scope is needed for being able to create a Google
Spreadsheet.

We are also setting the redirect_uri attribute of the flow object
to the same value that was specified during OAuth Configuration. Any
other value will result in an error.

Redirect user to the consent screen.

Next we ask the flow object for the authorization URL. In a web
application, you would redirect the user to this authorization URL so
they can login to their Google account and allow access. Or, for
testing purposes, you could just print the authorization URL, paste it
into a browser, and allow access acting as a user.

print(flow.authorization_url()[0])
Enter fullscreen mode Exit fullscreen mode

Collecting the access code from Google

The authorization URL leads to a Google server which takes the user
through a process of collecting the user's consent to allow access to
your application. It shows information about what exactly is being
requested and asks whether you want to allow access.

Once you agree to allow access, Google redirects the user again back
to the redirect_uri specified during OAuth configuration. Included
in the redirect URL is a code. Presumably, you have an application
running at that URL which can process the request parameters passed by
Google and extract the code parameter. This code represents user
allowing access to your application.

The following shows the URL invoked by Google after authorization is
successful. This example shows https://example.com as the website
domain, but your application would have its own domain.

url = 'https://example.com/oauth/callback/gsheets/?state=qf0xxxxxx8K7&code=4/0AQlxxxxxn4w&scope=email%20profile%20https://www.googleapis.com/auth/drive%20openid%20https://www.googleapis.com/auth/spreadsheets%20https://www.googleapis.com/auth/userinfo.profile%20https://www.googleapis.com/auth/userinfo.email&authuser=0&prompt=consent'
Enter fullscreen mode Exit fullscreen mode

Here is some code which processes the URL to extract the code passed
in by Google after user authorizes your application.

from urllib.parse import urlparse, parse_qsl, parse_qs, urlencode, urlunparse
code = parse_qs(urlparse(url).query)['code'][0]
Enter fullscreen mode Exit fullscreen mode

And this code can be passed on to the flow object created earlier to
retrieve the token which can be saved for future access. The token is
saved to a local file called token.json

token = flow.fetch_token(code=code)
with open('token.json', 'w') as fp:
    json.dump(token, fp, indent=2)
Enter fullscreen mode Exit fullscreen mode

Obtaining the refresh token

Google returns the refresh token along with the access token the first
time the user authorizes the application. However, if you would like
to make sure the refresh token is always included, you can pass the
additional parameter prompt=consent when redirecting the user to
Google for authorization.

Here is some code that can be used for the purpose. It mangles the
authorization URL returned by the flow object to add the parameter
prompt=consent before redirecting the user. The url printed on the
last line of this code fragment has prompt=consent included.

parse_res = urlparse(flow.authorization_url()[0])
qarr = parse_qsl(pres.query)
qarr.append(('prompt', 'consent'))
pres = pres._replace(query = urlencode(qarr))
url = urlunparse(pres)
print(url)
Enter fullscreen mode Exit fullscreen mode

Authenticating using the access token

At this point, we have two JSON files - a client_secret.json and a
token.json. We can now go ahead and use these to authenticate with
the Google API.

The first step is to import the required Google library.

import google.oauth2.credentials
Enter fullscreen mode Exit fullscreen mode

Next we declare a dictionary called cred_props in which we will
store the parameters required for authentication. We begin by copying
the access_token and refresh_token into this dictionary.

cred_props = {}
with open('token.json') as fp:
    x = json.load(fp)
    cred_props['access_token'] = x['access_token']
    cred_props['refresh_token'] = x['refresh_token']
Enter fullscreen mode Exit fullscreen mode

The library will use the access_token if it is still valid and
obtain a new one using the refresh_token if the access_token has
expired.

Next we load the contents of client_secret.json and pick up some
more parameters from it: token_uri, client_id and client_secret.

with open('client_secret.json') as fp:
    x = json.load(fp)
    cred_props['token_uri'] = x['web']['token_uri']
    cred_props['client_id'] = x['web']['client_id']
    cred_props['client_secret'] = x['web']['client_secret']
Enter fullscreen mode Exit fullscreen mode

With cred_props built as the library needs it, we obtain a
Credentials object as follows using the google.oauth2.credentials
module. Note that we pass both the access_token and the
refresh_token so the library can refresh when needed (as explained
earlier).

credentials = google.oauth2.credentials.Credentials(cred_props['access_token'],
                                                    refresh_token=cred_props['refresh_token'],
                                                    token_uri=cred_props['token_uri'],
                                                    client_id=cred_props['client_id'],
                                                    client_secret=cred_props['client_secret'])
Enter fullscreen mode Exit fullscreen mode

And this credentials object is what we need to invoke methods from
the Google spreadsheet API.

Using the Google Spreadsheet API

Let us now fetch some data from a Google spreadsheet using the Google
API. Begin by importing the required library.

from googleapiclient.discovery import build
Enter fullscreen mode Exit fullscreen mode

The way the API works is: obtain a service object for the desired
service - Google Docs or Google Drive or Google Spreadsheets or
anything else. Here we create a service object to access Google
spreadsheets.

service = build('sheets', 'v4', credentials=credentials)
Enter fullscreen mode Exit fullscreen mode

Using this service object and a spreadsheet ID, we can fetch the data
in the spreadsheet as follows. The spreadsheet ID is the component
between /d/ and /edit in the spreadsheet URL. For example, in the
following spreadsheet URL, the spreadsheet ID is 1Fouxxxxxxt88 (not
a real spreadsheet URL or an ID, just an example).

https://docs.google.com/spreadsheets/d/1Fouxxxxxxt88/edit?gid=0#gid=0
Enter fullscreen mode Exit fullscreen mode

Use the service object and the spreadsheet ID to obtain a result
object as follows:

result = service.spreadsheets().values().get(spreadsheetId=sheetId, range='Sheet1').execute()
Enter fullscreen mode Exit fullscreen mode

This call fetches the data in the sheet Sheet1 of the specified
spreadsheetId. Access values from the sheet as follows:

values = result.get("values", [])
if not values: raise Exception("No data found.")
for row in values: print(row)
Enter fullscreen mode Exit fullscreen mode

This fetches each row of data in the spreadsheet as a list. Do with
it what you will.

And that is how to use OAuth 2.0 to connect to the Google Spreadsheet
API and fetch values from a sheet.

Top comments (0)