Hei
I'm having trouble keeping my google OAuth Refresh Token valid for a small application I'm writing. I need to get data from a spreadsheet to a server / desktop application.
I'm trying to authorize with OAuth, which works for a week, then stops.
According to this post, this is expected behaviour:
https://stackoverflow.com/a/67966982/16509954
Another answer in the same thread posts a method how to permanently give access and not get your token expired:
https://stackoverflow.com/a/66292541/16509954
I did this but my token still keeps expiring.
Any ideas what I'm doing wrong?
I'm using the python library, my code is pretty much identical to the example given in the documentation quickstart.py:
https://developers.google.com/sheets/api/quickstart/python
Refresh tokens can expire for a number of reasons the main one these days being that your application is still in the testing phase.
Set your application over to production in Google cloud console and have it verified and the refresh tokens will not expire after a week.
from apiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
SCOPES = ['https://www.googleapis.com/auth/drive.drive']
KEY_FILE_LOCATION = '<REPLACE_WITH_JSON_FILE>'
VIEW_ID = '<REPLACE_WITH_VIEW_ID>'
def initialize_sheets():
"""Initializes an sheets service object.
Returns:
An authorized sheets service object.
"""
credentials = ServiceAccountCredentials.from_json_keyfile_name(
KEY_FILE_LOCATION, SCOPES)
# Build the service object.
service = build('sheets', 'v4', credentials=creds)
return service
Related
I'm trying to set up an app for personal use to send reminder emails to Google Calendar event attendees. According to Google's app verification exceptions, I should not need to verify an app for personal use. However, my access token keeps expiring after 7 days, and my attempts to refresh the token have been in vain. I realize that apps with publishing status of "Testing" expire after 7 days, but the only other option is to submit for verification. Perhaps the fact that Google Calendar API is defined as a sensitive scope means that I have to submit for verification even though I will be the only one using it?
Running the code below returns the error google.auth.exceptions.RefreshError: ('invalid_grant: Token has been expired or revoked.' My token.json file contains values for token and refresh_token.
reminders.py:
basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(basedir, '.env'))
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
def main():
"""Shows basic usage of the Google Calendar API.
Prints the start and name of the next 10 events on the user's calendar.
"""
flow = Flow.from_client_secrets_file(
os.path.join(basedir, 'credentials.json'), SCOPES)
authorization_url, state = flow.authorization_url(
# Enable offline access so that you can refresh an access token without
# re-prompting the user for permission. Recommended for web server apps.
access_type='offline',
# Enable incremental authorization. Recommended as a best practice.
include_granted_scopes='true')
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
os.path.join(basedir, 'credentials.json'), SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
service = build('calendar', 'v3', credentials=creds)
Your tokens are Expireing after seven days because your application is still in testing.
A Google Cloud Platform project with an OAuth consent screen configured for an external user type and a publishing status of "Testing" is issued a refresh token expiring in 7 days.
Solution would be to just set your project to production rather than testing. This should stop your tokens from expiring. You should not need to send it for verification just switch it to production. Nothing in the documentation nor testing by myself and others has shown that you "Must" go though verification to stop the refresh token expiration.
Internal
Internal projects only work for Google workspace accounts. So unless you have a workspace account then you cant set the project to internal. If you do have a workspace account and this user is on the workspace domain then you should consider using a service account instead. However if you choose to stick with oauth2 your refresh token should work for more then seven days if its internal however there is currently a bug that google is looking into which may cause the refresh token to expire on internal workspace projects. Last i heard they were looking into it.
Just for the sake of completeness, google verification of your production-app is not mandatory, there are exceptions, see:
https://support.google.com/cloud/answer/10311615?hl=en#zippy=%2Cin-production%2Cverification-not-required%2Cneeds-verification
Google verifies projects configured for a user type of External and a publishing status of In production if they meet one or more of the OAuth verification criteria:
You want to display an icon or display name for your project on the OAuth consent screen.
Your project's OAuth clients request authorization of any sensitive or restricted scopes.
The number of authorized domains for your project exceeds the domain count limit.
There are changes to your project's OAuth consent screen configuration after a previous published, verified configuration.
I had the same problem with my python app accessing gdrive-api, the solution was to set the app to production state and remove the logo/icon from oauth consent screen for bypassing verification.
After contacting Google regarding this issue, I learned that the only way to obtain a persistent token for the Google Calendar API while circumventing the app verification process is to purchase a Google Workspace account for $6 per month (at the time of this writing). You then have the option to mark the app as "Internal", which enables a persistent token. Initially it seemed that a free Google Cloud Identity account would be sufficient, but Google Calendar is disabled for the free tier.
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
I am using AllAuth on my Django app to manage user authentication. With that my users can connect their Google accounts and i get a google API token (with appropriate scopes).
I would like to use that token to access google APIs (Calendar v3 in my case) with that token, as my user already used OAuth2 to sign-in on my website, and gave me access to the calendar API.
Google only gives the full process on their website (from auth to api), is there a way to build my idea, or is it simply impossible?
i have tries drive = build('calendar', 'v3', credentials=credentials) as said here but "credentials" needs to be part of an oauth2client and not just a simple string token.
Thank you for your precious time.
Simon
I know its an older question but I finally found something that worked for me. After a successful authentication with allauth, I did this to get the client token and then build the service.
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
from allauth.socialaccount.models import SocialToken, SocialApp
...
# request is the HttpRequest object
token = SocialToken.objects.get(account__user=request.user, account__provider='google')
credentials = Credentials(
token=token.token,
refresh_token=token.token_secret,
token_uri='https://oauth2.googleapis.com/token',
client_id='client-id', # replace with yours
client_secret='client-secret') # replace with yours
service = build('calendar', 'v3', credentials=credentials)
GOAL:
List users in Google GSuite from GAE Standard python application using a service account
USING:
GAE Standard, Python , GSuite
STEPS PERFORMED:
https://developers.google.com/admin-sdk/directory/v1/guides/delegation
0. All steps list in this link 1.Created Service account, 2. enabled DoD OAuth 2.0 client ID 3. In gSuite security Enable API access 4. In gSuite Manage API client access added Authorized API clients & Scope
CODE:
from oauth2client.service_account import ServiceAccountCredentials
import httplib2
from googleapiclient import discovery
import oauth2client
def main():
"""Shows basic usage of the Google Admin SDK Directory API. Creates a Google Admin SDK API service object and outputs a list of first 10 users in the domain."""
credentials = ServiceAccountCredentials.from_json_keyfile_name(‘keys/key.json',
scopes=['https://www.googleapis.com/auth/admin.directory.user'])
credentials = credentials.create_delegated(‘gsuite.admin.email#domian.com’)
http = credentials.authorize(httplib2.Http())
service = discovery.build('admin', 'directory_v1', http=http)
print('Getting the first 10 users in the domain')
results = service.users().list(customer='my_customer', orderBy='email').execute()
ERROR:
HttpAccessTokenRefreshError: unauthorized_client: Client is unauthorized to retrieve access tokens using this method.
Thank you, appreciate any examples that work.
SOLUTION/ISSUE RESOLUTION
Make sure you have the EXACT SCOPE in the gSuite Manage API client access Authorized API clients & Scope, had 20+ scopes in my but somehow did not have this one.
I'm writing a small python script that will retrieve a list of my Google Contacts (using the Google Contacts API) and will randomly suggest one person for me to contact (good way to automate keeping in touch with friends!)
This is just a standalone script that I plan to schedule on a cron job. The problem is that Google seems to require OAuth2 style authentication, where the user (me) has to approve the access and then the app receives an authorization token I can then use to query the user's (my) contacts.
Since I'm only accessing my own data, is there a way to "pre-authorize" myself? Ideally I'd love to be able to retrieve some authorization token and then I'd run the script and pass that token as an environment variable
AUTH_TOKEN=12345 python my_script.py
That way it doesn't require user input/interaction to authorize it one time.
The implementation you're describing invokes the full "three-legged" OAuth handshake, which requires explicit user consent. If you don't need user consent, you can instead utilize "two-legged" OAuth via a Google service account, which is tied to an application, rather than a user. Once you've granted permission to your service account to access your contacts, you can use the oauth2client ServiceAccountCredentials class to directly access GData without requiring user consent.
Here's the two-legged authentication example from the Google service account documentation:
import json
from httplib2 import Http
from oauth2client.service_account import ServiceAccountCredentials
from apiclient.discovery import build
scopes = ['https://www.googleapis.com/auth/sqlservice.admin']
credentials = ServiceAccountCredentials.from_json_keyfile_name(
'service-account.json', scopes)
sqladmin = build('sqladmin', 'v1beta3', credentials=credentials)
response = sqladmin.instances().list(project='examinable-example-123').execute()
print response