I'm writing a basic app that prints out projects in Google's Cloud Resources Manager using this method:
https://cloud.google.com/resource-manager/reference/rest/v1/projects/list
Yesterday it worked but I revoked the token and the code doesn't prompt to re-authorize.
from googleapiclient import discovery from oauth2client.client import GoogleCredentials
credentials = GoogleCredentials.get_application_default()
service = discovery.build('cloudresourcemanager', 'v1', credentials=credentials)
projects = service.projects() request = projects.list() while request is not None:
response = request.execute()
for project in response['projects']:
print project
request = projects.list_next(previous_request=request, previous_response=response)
File "oauth2client/client.py", line 834, in _do_refresh_request
raise HttpAccessTokenRefreshError(error_msg, status=resp.status) oauth2client.client.HttpAccessTokenRefreshError: invalid_grant: Token
has been revoked.
I think there's a way to tell the client to check if the token is valid and pop the user out to a browser if not, but can't seem to get the code to do it. Any help appreciated ;)
Since this code uses application default credentials, the gcloud command is how I get a new token:
gcloud beta auth application-default login
Although it would be nice if there was a way to do this in code in the event the token is revoked again.
Suddenly yesterday we faced a similar issue in Google OAuth2 connection from gerrit code review. So just wanted to share this as it might be related to you. Refer this github issue & its commit made for gerrit-oauth-provider
Related
I'm using gspread in a python app to access some backend Google Sheets that act as a database for the application. The user is required to log into the app using an in app browser and an authlib OAuth2 session is created using the token returned in the url via this method. The initial log in and access works fine, but when the token times out after an hour I access the sheets anymore and the app breaks.
I can't work out how to refresh the token using authlib, the reason we picked the library is because it integrated with gspread and the it was supposed to autorefresh auth tokens for you. The code that I use for login is below but after an hour the gspread function open_by_key() fails because of the authentication problem.
I have tried recreating the gspread client, but that just uses the same token. I feel like there should be a way to refresh the token to extend its life for another hour but I can't find how to do that with authlib.
creds = OAuth2Session(clientId, clientSecret, scope=scope, redirect_uri=redirectUrl)
authUrl, state = creds.create_authorization_url('https://accounts.google.com/o/oauth2/v2/auth', response_type='token')
Load the authURL in the browser and get user to log in. Browser returns the authentication response as urlString
creds.fetch_access_token(authorization_response=urlString)
gc = gspread.Client(None, creds)
ss = gc.open_by_key(sheetKey)
todaysSheetName = datetime.datetime.now().strftime('%d-%m-%Y')
wks = ss.worksheet(todaysSheetName)
authUrl, state = creds.create_authorization_url('https://accounts.google.com/o/oauth2/v2/auth', response_type='token')
According to this code you are using, response_type=token is an implicit grant type flow. I guess there won't be a refresh_token in token with implicit flow. Here are my suggestions:
try change it to authorization code flow
use a database to save tokens
if the token is expired, use your previously saved token's refresh_token to exchange a new token.
The refresh_token method is simple:
refresh_token(url=None, refresh_token=None, body='', auth=None, headers=None, **kwargs)
Using your code, it should be:
refresh_token = your_previous_token.refresh_token
new_token = creds.refresh_token('https://accounts.google.com/...', refresh_token=refresh_token)
The problem is that there is no documentation on this part. But according to the API, it would be used like this. If it is not working, you can report it to Authlib issue tracker.
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)
i'm trying to write a simple script that will get the users list from my Google G Suite domain from Directory API of Admin SDK using google-api-python-client. I've read tons of documentation, tried hundreds of various requests, but always receive: googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/admin/directory/v1/users?domain=example.com&alt=json returned "Not Authorized to access this resource/api"> error.
This is what i did:
In the Google Developer console https://console.developers.google.com:
created a new project
enabled 'Admin SDK' API.
created a Service account Key
saved the generated key into a 'service-key.json' file
In the G Suite Admin console:
API access is enabled
Admin SDK is enabled
'Client ID' of the service key ^^ is authorized to 'View users on your domain', scope: https://www.googleapis.com/auth/admin.directory.user.readonly in the API client access console section.
Created a simple test script:
#!/usr/bin/env python3
import json
from httplib2 import Http
from oauth2client.service_account import ServiceAccountCredentials
from apiclient.discovery import build
scopes = ['https://www.googleapis.com/auth/admin.directory.user.readonly']
credentials = ServiceAccountCredentials.from_json_keyfile_name(
'service-key.json', scopes)
account = credentials.authorize(Http())
service = build('admin', 'directory_v1', http=account)
response = service.users().list(domain='example.com').execute()
print(response)
Other:
tried also 'Enable G Suite Domain-wide Delegation' (used create_delegated() method on a ServiceAccountCredentials object)
i see in the Google Developer Console - Dashboard, that the script is issuing the proper requests - can see the 'directory.users.list' API methods are being issued, but fails with 403 error
Thanks in advance for any help!
#Mr.Rebot's suggestion worked for me. The basic service account wouldn't work but impersonating the admin (which you have enabled it to do), allows the API call to pass.
I'm trying to connect to my Google Cloud Endpoints API that is running as an Appengine app:
#endpoints.api(name='helloworldendpoints', allowed_client_ids=["1234", "12345"], version='v1', auth_level=endpoints.AUTH_LEVEL.REQUIRED)
class HelloWorldApi(remote.Service):
...
The API request is as follows:
scopes = ["https://www.googleapis.com/auth/userinfo.email"]
credentials = ServiceAccountCredentials.from_json_keyfile_name("CloudEndpointsClient.json", scopes)
from httplib2 import Http
http_auth = credentials.authorize(Http())
from apiclient.discovery import build
api_root = 'https://myapp.appspot.com/_ah/api'
api = 'helloworldendpoints'
version = 'v1'
discovery_url = '%s/discovery/v1/apis/%s/%s/rest' % (api_root, api, version)
service = build(api, version, discoveryServiceUrl=discovery_url)
response = service.myFunction(myparameter = "123456").execute(http=http_auth)#
print response
The requests work well if I remove authentication requirements.
I know that authentication works since the error changes if after authenticating.
The error message I'm getting is:
googleapiclient.errors.HttpError: https://my-app.appspot.com/_ah/api/helloworldendpoints/v1/obtainScoreFromEmail?myparameter=1234&alt=json returned "Access Not Configured. has not been used in project 123456789 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/helloworldendpoints/overview?project=123456789 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.">
I cannot enable the API in my Google Cloud Project, since the API does not exist.
What I found to work is a hack around. I used a user authentication (instead of server) on the same project for the same API which worked (https://cloud.google.com/endpoints/docs/frameworks/python/access_from_python).
After I switched back to my initial server auth. method it started working.
I am trying to authorize my app using google plus. However, everything I've tried always gives me a 404 when I use the people service and attempt to get 'me', except when I log in with the user who created the Google+ API app. It doesn't make any sense to my why that would be the case.
from httplib2 import Http
from apiclient.discovery import build
credential = gp_client.step2_exchange(request.REQUEST)
http = Http()
http = credential.authorize(http)
service = build("plus", "v1", http=http)
o = service.people().get(userId='me').execute()
The same is also true when I use the Google+ explorer:
http://code.google.com/apis/explorer/#_s=plus&_v=v1&_m=people.get&fields=emails
If I switch to private with the user who created the API, then I can fetch 'me', but if I authorize another user, then I can't fetch 'me'.
Any ideas what is happening?
Finally figured out what this was. Google lets you authorize any google account for an app. However, if the authorizing user hasn't upgraded to Google+, when the app makes its first Google+ API request, the request will 404. This is mind boggling, because when I setup the OAuth2WebServerFlow I specified "plus.me" as the scope... IMHO Google should tell the user to upgrade to Google+ when they try to authorize this OAuth request.
gp_client = OAuth2WebServerFlow(
settings.GOOGLE_PLUS_CONSUMER_ID,
settings.GOOGLE_PLUS_CONSUMER_SECRET,
scope='https://www.googleapis.com/auth/plus.me',
)