I want to be able to read a spreadsheet, that has been shared with me, inside the company I work.
I have tried adapting the script from this link.
This is the code I use:
from apiclient import discovery
from oauth2client.service_account import ServiceAccountCredentials
import httplib2
scope = [
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive'
]
SERVICE_ACCOUNT_FILE = "gsheetread-293005-d7e75122e4c7.json"
credentials = ServiceAccountCredentials.from_json_keyfile_name(
SERVICE_ACCOUNT_FILE, scopes=scope)
# Use the create_delegated() method and authorize the delegated credentials
delegated_credentials = credentials.create_delegated('myemail#company.com')
delegated_http = delegated_credentials.authorize(httplib2.Http())
google_sheet = discovery.build('spreadsheet_id', 'v3', http=delegated_http)
I get an error:
oauth2client.client.HttpAccessTokenRefreshError: unauthorized_client: Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.
The error you're getting means you didn't add some or all of the specified scopes (https://www.googleapis.com/auth/spreadsheets,
https://www.googleapis.com/auth/drive) when granting domain-wide authority to your service account. Please follow these steps:
In step 5, you have to add both https://www.googleapis.com/auth/spreadsheets,
https://www.googleapis.com/auth/drive (actually, you only need to add one, if you're just accessing a spreadsheet, but you should remove the other one from your code).
Additional notes:
The library you're using, oauth2client, is deprecated. Use google-auth instead, which doesn't rely on httplib2.
You're not using a valid service name (spreadsheet_id). If you want to use Sheets API, the service name should be sheets.
Sheets API V3 is deprecated and will be shut down in January 2021. Use V4 instead.
As I mentioned before, there's no need to use the drive scope if you're accessing a spreadsheet. spreadsheets is enough.
Code snippet (using non-deprecated library):
from googleapiclient.discovery import build
from google.oauth2 import service_account
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
SERVICE_ACCOUNT_FILE = "gsheetread-293005-d7e75122e4c7.json"
creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
delegated_credentials = creds.with_subject('myemail#company.com')
service = build('sheets', 'v4', credentials=delegated_credentials)
spreadsheet = service.spreadsheets().get(spreadsheetId="YOUR_SPREADSHEET_ID").execute()
print(spreadsheet)
Related
I am writing a script that will authenticate to the Gmail API, pull some emails and transform some email data. I can get this working locally since I have the service account file which I am creating a credentials object from and then referencing in the Gmail API, however since this will be running in Google Cloud Product (GCP) the credentials are stored in the environment.
I need to somehow either modify the code to not reference credentials or retrieve a credentials object in the environment.
The code below works locally, however where I am creating the credentials object, I either need to retrieve that from the environment or authenticate in a different way when creating the delegated_credentials and service objects.
from googleapiclient.discovery import build
from google.oauth2 import service_account
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
SERVICE_ACCOUNT_FILE = 'secret.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
delegated_credentials = credentials.with_subject('someone#gmail.com')
service = build('gmail', 'v1', credentials=delegated_credentials)
As an example, when using the google cloud storage API, I create the client in GCP as follow:
storage_client = storage.Client()
I write a python script to upload file to google drive, but the script is redirecting to chrome for email user authentication.
is there any way to avoid redirecting to chrome for authentication.
I'm running on python 3.9.
here is my sample code:
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
gauth = GoogleAuth()
drive = GoogleDrive(gauth)
upload_file_list = ['myfile.pdf']
for upload_file in upload_file_list:
gfile = drive.CreateFile({'parents': [{'id': '1B8ttlQMRUkjbrscevfa1DablIayzObh2'}]})
# Read file and set it as the content of this instance.
gfile.SetContentFile(upload_file)
gfile.Upload() # Upload the file.
The behaviour you are reporting is totally normal with OAuth 2.0 and the official Google APIs library.
What #Tanaike said is a good solution. You could use a service account to access Google Drive files without granting consent every time the token expires. With service accounts there are 2 options to achieve that:
Share the file/folder with the email address of the service account.
Use domain-wide delegation of authority to allow the service account to impersonate any user in your domain. Requires a domain using Google Workspace or Cloud Identity and Super Admin access to configure domain-wide delegation.
General information on how to make API calls with domain-wide delegation is available on this page https://developers.google.com/identity/protocols/oauth2/service-account#authorizingrequests.
Here is a working code sample:
from google.oauth2 import service_account
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# Scopes required by this endpoint
# https://developers.google.com/drive/api/v3/reference/permissions/list
SCOPES = ["https://www.googleapis.com/auth/drive.readonly"]
# Variable that holds the file ID
DOCUMENT_ID = "i0321LSy8mmkx_Bw-XlDyzQ_b3Ny9m74u"
# Service account Credential file downloaded with domain-wide delegation of authority
# or with shared access to the file.
SERVICE_ACCOUNT_FILE = "serviceaccount.json";
# Creation of the credentials
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE,
scopes=SCOPES)
# [Impersonation] the service account will take action on behalf of the user,
# requires domain-wide delegation of authority.
delegated_credentials = credentials.with_subject('user#domain.com')
# The API call is attempted
try:
service = build('drive', 'v3', credentials=delegated_credentials)
# Retrieve the documents contents from the Docs service.
document = service.files().get(fileId=DOCUMENT_ID).execute()
print('The title of the document is: {}'.format(document.get('name')))
except HttpError as err:
print(err)
Keep in mind that to use user impersonation you will need to configure domain-wide delegation in the Admin console of the domain that has the files (this will also work for external files shared with users in the domain).
If you want to use this with regular consumer accounts you can't use user impersonation, instead you will share the file with the service account (read or write access) to later make API calls. Line 20 creates delegated credentials, this line needs to be removed if you will use this other approach.
I'm trying to build a directory sync ETL for Google Workspace, but I'm getting 403's from the code snippet.
from google.oauth2 import service_account
from googleapiclient.discovery import build
SCOPES = ['https://www.googleapis.com/auth/admin.directory.user.readonly']
SERVICE_ACCOUNT_FILE = './credentials.json' #TODO: these creds need to be passed in more safely.
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
service = build('admin', 'directory_v1', credentials=credentials)
results = service.users().list(domain='mydomain.com').execute()
users = results.get('users', [])
The service account has been given domain-wide delegation to the listed scope and should be able to access the API. Other similar posts have mentioned that a domain administrator must approve the request, but that doesn't make sense in the case where I need this to run multiple times a week without any administrator intervention.
Using the Users API requires the User Management Admin role (or an equivalent custom role). You can grant this role to a service account, then you won't need domain-wide delegation at all.
I have a spreadsheet on our company Google Drive that I would like to be read by a service account. I've been stuck on this for some time now but I can't figure out what I'm missing.
For that I created a service account, enabled the domain wide delegation and added the needed scopes ('https://www.googleapis.com/auth/drive' and 'https://www.googleapis.com/auth/spreadsheets.readonly') in the G Suite Admin panel, matching the client id of my service account. I created a key for this service account and save it in a json file (file is key_sa.json).
Despite that, when running the following code, it fails:
SERVICE_ACCOUNT_JSON_FILE_PATH = 'key_sa.json'
SCOPES = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/spreadsheets.readonly']
credentials = ServiceAccountCredentials.from_json_keyfile_name(
SERVICE_ACCOUNT_JSON_FILE_PATH, scopes=SCOPES)
delegated_credentials = credentials.create_delegated('user#company.com')
delegated_http = delegated_credentials.authorize(httplib2.Http())
delegated_credentials.refresh(delegated_http)
service = build('sheets', 'v4', http=delegated_http) # credentials=credentials)
The error message I got for the last line is:
oauth2client.client.HttpAccessTokenRefreshError: unauthorized_client: Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.
Any hint as to what I'm missing?
Try adding the following scopes as mentioned below:
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/spreadsheets.readonly',
'https://www.googleapis.com/auth/spreadsheets'
These scopes must be added to both in your code and the API permissions in Google Admin Security setting.
...spreadsheets.readonly
...spreadsheets
both the above permissions behave differently and so both need to be added.
I am trying to fetch gsuite alerts via API. I have created a service account as per their docs and I have assigned that service account to my google cloud function.
I do not want to use environment variables or upload credentials along with source code but I want leverage default service account used by function.
from googleapiclient.discovery import build
def get_credentials():
# if one knows credentials file location(when one uploads the json credentials file or specify them in environment variable) one can easily get the credentials by specify the path.
# In case of google cloud functions atleast I couldn't find it the path as the GOOGLE_APPLICATION_CREDENTIALS is empty in python runtime
# the below code work find if one uncomments the below line
#credentials = ServiceAccountCredentials.from_json_keyfile_name(key_file_location)
credentials = < how to get default credentials object for default service account?>
delegated_credentials = credentials.create_delegated('admin#alertcenter1.bigr.name').create_scoped(SCOPES)
return delegated_credentials
def get_alerts(api_name, api_version, key_file_location=None):
delegated_credentials = get_credentials()
alertcli = build(api_name, api_version, credentials=delegated_credentials)
resp = alertcli.alerts().list(pageToken=None).execute()
print(resp)
Is there any way I can create a default credentials object. I have tried using
from google.auth import credentials but this does not contain create_delegated function and
I have also tried ServiceAccountCredentials() but this requires signer.
Here is an example to use the Gmail API with delegated credentials. The service account credentials will need "Enable G Suite Domain-wide Delegation" enabled.
from google.oauth2 import service_account
from googleapiclient.discovery import build
credentials = service_account.Credentials.from_service_account_file(
credentials_file,
scopes=['https://www.googleapis.com/auth/gmail.send'])
impersonate = 'username#example.com'
credentials = credentials.with_subject(impersonate)
service = build('gmail', 'v1', credentials=credentials)
You can use the google.auth.default function to get the default credentials and use them to make an IAM signer which can be used to create new service account credentials which has the delegated email adress as subject. I have a more detailed answer for a similar question.
There is also Google Cloud Platform Github repository with some documentation about this method.