Unable to authenticate Google API with Oauth2 - python

I had a Python script that I had authorized to access a Google Spreadsheet. But when now when I run it, it throws the exception:
oauth2client.client.AccessTokenRefreshError: invalid_client: The OAuth client was not found.
My auth code looks like:
import json
import gspread
from oauth2client.client import SignedJwtAssertionCredentials
json_key = json.load(open('myapp-credentials.json'))
scope = ['https://spreadsheets.google.com/feeds']
credentials = SignedJwtAssertionCredentials(
json_key['client_email'],
json_key['private_key'], scope)
gc = gspread.authorize(credentials)
I've not changed anything in the account, so I don't know why it would suddenly stop working. If I login to https://console.developers.google.com/apis/credentials, I see the app is still registered. How do I diagnose this and re-enable its access? Googling this error unfortunately finds dozens of potential causes.

The class SignedJwtAssertionCredentials has been removed from the source code as of February 5, 2016, and its functionality replaced with that of ServiceAccountCredentials. This may be causing your error.
It appears that you'll want to use something like the following to generate your credentials:
credentials = ServiceAccountCredentials.from_json_keyfile_name('myapp-credentials.json', scope)
This Github issue thread might be helpful, as well.

Related

Can't seem to impersonate user or not getting token with google oauth2 in python using a service account

Very new to the Google API and Python but have had some success with some small actions like getting files under google drive with my own creds etc
I am a Google Workspace admin, I have granted domain-wide delegation to the service account with the full access drive scope: https://www.googleapis.com/auth/drive
domain-wide delegation
The scope is also granted under the oauth consent screen for the same scope: https://www.googleapis.com/auth/drive
oauth consent screen
I also added them in the right order as that can be an issue sometimes apparently on researching this all
The issue I am having is I am wanting to impersonate a user to get a list of their files for an ongoing project and after lots of battling on I am now not getting any errors but don't seem to be impersonating the user correctly and/or not getting a token perhaps?
This could be me being a noob still at this but don't understand why I can't seem to impersonate a user and see their files still using the code below.
I have trawled through my posts on here with people asking similiar questions but not really found an answer yet I'm afraid so wanted to ask this now.
The code I am trying to use is as follows:
from __future__ import print_function
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.oauth2 import service_account
credentials = service_account.Credentials.from_service_account_file('serviceaccount.json')
scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/drive'])
delegated_credentials = scoped_credentials.with_subject('someone#company.com')
print(delegated_credentials.token)
drive = build('drive', 'v3', credentials = delegated_credentials)
drive.files().list().execute()
if __name__ == '__main__':
main()
The output I am getting is:
python.exe .\impersonate.py
None
So no errors, but I'm also not getting the expected results of perhaps seeing a token (if that's how it's supposed to work), and at least I thought I'd be now seeing the files and folders of the user in the subject I had specified.
Any help would be greatly appreciated!
Ok so not too long after I posted this I saw something else I thought I should try in the code and it's worked!
A little embarrassing but this may help someone else in the future with this issue :)
The fix for me was adding : delegated_credentials.refresh(Request())
credentials = service_account.Credentials.from_service_account_file('serviceaccount.json')
scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/drive'])
delegated_credentials = scoped_credentials.with_subject('someone#company.com')
delegated_credentials.refresh(Request())
print(delegated_credentials.token)
I am now able to see the users files and folders successfully

Google OAuth client is using the wrong project_id from the json file- Python

My Python (3.6.7) code uses oauth2client to access Google Photos APIs. It successfully authenticates, but when it tries to access the Google Photos albums, it seems to be using the username as the project_id.
from __future__ import print_function
from apiclient.discovery import build
from httplib2 import Http
from oauth2client import file, client, tools
# Setup the Photo v1 API
SCOPES = 'https://www.googleapis.com/auth/photoslibrary.readonly'
store = file.Storage('credentials.json')
creds = store.get()
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('scripts/client_id.json', SCOPES)
creds = tools.run_flow(flow, store)
service = build('photoslibrary', 'v1', http=creds.authorize(Http()))
# Call the Photo v1 API
results = service.albums().list(
pageSize=10, fields="nextPageToken,albums(id,title)").execute()
items = results.get('albums', [])
if not items:
print('No albums found.')
else:
print('Albums:')
for item in items:
print('{0} ({1})'.format(item['title'].encode('utf8'), item['id']))
When executing the above code, it prompts me the auth page. When I successfully authenticate, it shows me the following error:
HttpError 403 when requesting {URL} returned "Photos Library API has not been used in project 123456 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/photoslibrary.googleapis.com/overview?project=123456 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.">
Interestingly, the number in bold 123456 (obviously changed) is actually the first part of the client_id found in the client_id.json
But the project_id looks something like this: test1-235515
So what I got from this error is that the oauth2client client is passing the client_id instead of the project_id. So even though I have enabled the Photos API, it will never access it correctly.
Please help with this error. How can I manually change the project_id?
The project ID is different from the project number. You will be able to see both in your Google Cloud Console configuration. See this documentation for more on how to identify your projects [1].
A single Google Cloud project can have many different OAuth client IDs configured. See this documentation for information about creating OAuth client credentials [2]. You should be only have to make sure that the client you created belongs to the project for which you have enabled APIs. Going to the URL provided in the error message should take you to the right configuration page.
[1] https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects
[2] https://support.google.com/cloud/answer/6158849?hl=en

Google Sheets - Python Access timeout

I'am trying to read and write from and to a Google Spreadsheet. I've found this tutorial online, which seems to work for a lot of people:
https://www.twilio.com/blog/2017/02/an-easy-way-to-read-and-write-to-a-google-spreadsheet-in-python.html?utm_source=youtube&utm_medium=video&utm_campaign=youtube_python_google_sheets
What I did so far:
Created the credentials according to the guide and downloaded the .json file
Shared a copy of the Legislator Spreadsheet (now in my google Drive) with the client e-mail.
I received an E-Mail after sharing that the message wasn't delivered as the service account domain wasn't found.
I adopted the code
import gspread
from oauth2client.service_account import ServiceAccountCredentials
scope = ['https://spreadsheets.google.com/feeds']
creds = ServiceAccountCredentials.from_json_keyfile_name('client_secret.json', scope)
client = gspread.authorize(creds)
sheet = client.open("Copy of Legislators 2017", scope)
list_of_hashes = sheet.get_all_records()
print(list_of_hashes)
The client_secret.json file is in the same folder as the project.
But when I run the code, I get the following error:
The Japanese says: ”It was not possible to connect because the connected callee did not respond correctly even after a certain period of time. Or the connected host failed because the connected host did not respond.”
There seems to be a problem with the log in, but I have no clue how to fix it.
Also, while researching, I found that the scope part is always different. Can someone explain to me what needs to be insert there?
Does anyone have an experience with it? Thank you for your help :)
Make sure you're OAuthclientID is valid and you've enabled Sheets API in your Google Dev Console.
Then, try to use the Sheetsv4 scope instead. I remember encountering some errors when I used the scope for v3. So hopefully, this helps you:
import gspread
from oauth2client.service_account import ServiceAccountCredentials
import pprint
scope = ['https://www.googleapis.com/auth/spreadsheets']
creds = ServiceAccountCredentials.from_json_keyfile_name('client_secret.json', scope)
client = gspread.authorize(creds)
sheet = client.open('NAME_OF_GOOGLE_SHEET').sheet1
pp = pprint.PrettyPrinter()
# get all the records
result = sheet.get_all_records()
pp.pprint(result)

Accessing google spreadsheet using shared link

I am building a python app where user can provide 'Anyone can edit' link to their spreadsheet and data can be read from there. I am using python's gspread module but it throws SpreadSheetNotFound error.
import gspread
from oauth2client.service_account import ServiceAccountCredentials
scope = ['https://spreadsheets.google.com/feeds']
credentials = ServiceAccountCredentials.from_json_keyfile_name('myauth.json', scope)
gc = gspread.authorize(credentials)
wks = gc.open_by_url(urls_given_by_user_having_edit_permission)
Above code is not working. Is there any way to achieve this?
wks.share('', perm_type='anyone', role='reader')
Read permission for all people with link. reference https://gspread.readthedocs.io/en/latest/api.html#gspread.models.Spreadsheet.share

Error 404 on creating a calendar with Google Calendar Api

I just wrote this code that is supposed to check if calendar exists and if not create one. Well it returns error 404 when I try to create a calendar and the calendar does NOT appear. Any ideas? I blanked out clientid, secret, app key.
import gflags
import httplib2
import sys, traceback
from apiclient.discovery import build
from oauth2client.file import Storage
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.tools import run
FLAGS = gflags.FLAGS
# Set up a Flow object to be used if we need to authenticate. This
# sample uses OAuth 2.0, and we set up the OAuth2WebServerFlow with
# the information it needs to authenticate. Note that it is called
# the Web Server Flow, but it can also handle the flow for native
# applications
# The client_id and client_secret are copied from the API Access tab on
# the Google APIs Console
FLOW = OAuth2WebServerFlow(
client_id='MY_CLIENT_ID',
client_secret='MY_SECRET',
scope='https://www.googleapis.com/auth/calendar',
user_agent='KUDOS_CALENDAR/v1')
# To disable the local server feature, uncomment the following line:
# FLAGS.auth_local_webserver = False
# If the Credentials don't exist or are invalid, run through the native client
# flow. The Storage object will ensure that if successful the good
# Credentials will get written back to a file.
storage = Storage('calendar.dat')
credentials = storage.get()
if credentials is None or credentials.invalid == True:
credentials = run(FLOW, storage)
# Create an httplib2.Http object to handle our HTTP requests and authorize it
# with our good Credentials.
http = httplib2.Http()
http = credentials.authorize(http)
# Build a service object for interacting with the API. Visit
# the Google APIs Console
# to get a developerKey for your own application.
service = build(serviceName='calendar', version='v3', http=http,
developerKey='MY_DEV_KEY')
kudos_calendar = None
try:
kudos_calendar = service.calendarList().get(calendarId='KudosCalendar').execute()
except:
print 'Calendar KudosCalendar does not exist!'
print 'Creating one right now...'
kudos_calendar_entry = {
'id': 'KudosCalendar'
}
kudos_calendar = service.calendarList().insert(body=kudos_calendar_entry).execute()
OK, I found a way around. I am not sure what exactly are google abstractions reflecting, but I am pretty sure one cannot just create calendar list. However if you just create a calendar then everything goes fine and then one can use calendar id to access calendarlist entry corresponding to that calendar.
Ufff.. Horribly confusing. Also while trying to do that I found at least two bugs in example python codes given in docs. I think they still did not properly rolled out v3.

Categories