Get refresh token using service account impersonation Google Drive

Viewed 606

I wrote a script that will search for files in multiple users gdrives in our company. The problem is that the script works but only for a period of time, but then it gets HTTP 401 errors, most likely due to the access token expiring. I'm using a service account with domain wide delegation enabled, and using google.oauth2 python library to create a Drive service object (See below code). I wanted to programmatically get a refresh token and generate new access tokens when current ones expire. The problem is there is no documentation on how to do that using service accounts. There is with normal user interactive oauth flows. My question is how would I use the drive object to retrieve a refresh token and then create new access tokens. I know I will have to use my service account's private key somehow but not sure.

I've been reading these two docs. https://developers.google.com/identity/protocols/oauth2 https://developers.google.com/identity/protocols/oauth2/service-account

  • Creating a drive service object with an impersonated user
from google.oauth2 import service_account
import googleapiclient.discovery

def impersonate_user(user_to_impersonate, key_file, domain):
        scopes = ['https://www.googleapis.com/auth/drive',]

        if user_to_impersonate is None:
            raise InvalidDomainUser(f"{user_to_impersonate} is not a member of {domain}")

        if key_file is None:
            raise FileNotFoundError(f"{key_file} is not a valid service account file")

        delegated_credentials = service_account.Credentials.from_service_account_file(
            key_file, scopes=scopes)
        # Impersonate User.
        delegated_credentials = delegated_credentials.with_subject(user_to_impersonate)
        drive_service = googleapiclient.discovery.build('drive', 'v2', credentials=delegated_credentials)

        # Set new drive resource object
        return drive_service
  • Listing files in users drive
service = impersonate_user(user_to_impersonate, key_file, domain):
children = service.children().list(folderId=folderId,maxResults=1000, **param).execute() 
for child in items:
            item = service.files().get(fileId=child['id']).execute()
            print(item.get("title", None))


2 Answers

Impersonating a user generates an access token which expires. To get a new access token, you simply generate one the same way you did the first time. Service Accounts don't have a notion of a refresh token, you just generate a new access token using your credentials.

Source: built Google Service Account support at Xkit

This is the basic code for service account with python. You will need to add the delegation part.

As long as you use the same service the library should be fetching you a new access token when ever it needs one. It should not be expiring.

"""Hello drive"""

from apiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials


SCOPES = ['https://www.googleapis.com/auth/drive.readonly']
KEY_FILE_LOCATION = '<REPLACE_WITH_JSON_FILE>'
VIEW_ID = '<REPLACE_WITH_VIEW_ID>'


def drive():
  """Initializes an drive service object.

  Returns:
    An authorized drive service object.
  """
  credentials = ServiceAccountCredentials.from_json_keyfile_name(
      KEY_FILE_LOCATION, SCOPES)

  # Build the service object.
  drive = build('drive', 'v3', credentials=credentials)

  return drive
Related