2

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))


Brpat
  • 63
  • 4
  • 1
    When you say "the script works but only for a period of time" how much time in specific? After you create service, do you use it only once for listing? Or do you use the same service object again after an extended amount of time? – ziganotschka Jan 20 '21 at 08:46
  • Hey it expires in about 1hr. 3600 seconds. After that each api call results in a 401 error. I use the same drive object for a period of time UNLESS, the file or folder inside that user's drive is owned by someone else, in which case I will re-instantiate that object by impersonating a new user – Brpat Jan 20 '21 at 17:55
  • 1
    Why don't you create a new service object each time you need it (after extended time)? – ziganotschka Jan 20 '21 at 22:00
  • I have the logic the re-instantiate the service object at intervals, but I still get this error File "E:\Scripts\My_Scripts\Scripts\gdrive_enum\lib\site-packages\googleapiclient\http.py", line 898, in execute raise HttpError(resp, content, uri=self.uri) googleapiclient.errors. ```HttpError: ``` – Brpat Jan 21 '21 at 00:22
  • There must be some problem somewhere in your logic, can you provide the code part where you re-instantiate? – ziganotschka Jan 21 '21 at 08:26
  • 1
    Also, make sure that `user_to_impersonate` is always a user from the same domain like the service account. You might pass it an empty value. But actually your case sounds like inspite of creating service acocunt credentails, your code uses an access token instead for creating the service object. – ziganotschka Jan 21 '21 at 08:41
  • 1
    Hey so I got it working now. It was a logic error in my code. But it seems to be working now. Appreciate the help! – Brpat Jan 21 '21 at 13:45

2 Answers2

0

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

Trey Griffith
  • 499
  • 4
  • 8
0

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
Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
  • Hello, yes it still seems to expire after 1 hour. I'm getting this error ``` ``` In the File lib\site-packages\googleapiclient\http.py – Brpat Jan 21 '21 at 00:24