34

I am using googles official oauth2client.client to access the google plus api. I have a refresh token (that does not expire) stored in a database, and need to recreate the temporary "Credentials" (access token) from that.

But I could not find a way to do this with to official library supplied by google.

So I hacked around it: used urllib to access the API that gives me a new access_token from the refresh_token. Using the access_token I can then use the library.

I must be missing somthing!

from apiclient import discovery from oauth2client.client import AccessTokenCredentials from urllib import urlencode from urllib2 import Request , urlopen, HTTPError import json # ========================================== def access_token_from_refresh_token(client_id, client_secret, refresh_token): request = Request('https://accounts.google.com/o/oauth2/token', data=urlencode({ 'grant_type': 'refresh_token', 'client_id': client_id, 'client_secret': client_secret, 'refresh_token': refresh_token }), headers={ 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' } ) response = json.load(urlopen(request)) return response['access_token'] # ========================================== access_token = access_token_from_refresh_token(CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN) # now I can use the library properly credentials = AccessTokenCredentials(access_token, "MyAgent/1.0", None) http = credentials.authorize(httplib2.Http()) service = discovery.build('plus', 'v1', http=http) google_request = service.people().get(userId='me') result = google_request.execute(http=http) 
2
  • 4
    Hi, did you find a solution to refresh the token while using AccessTokenCredentials ? I am trying to find out how to do that but there is no visible documentation around ... Commented Jan 31, 2016 at 7:50
  • 2
    Just to make a dent in the history, in case somebody else is hitting his/her head to the wall: Google's documentation is complicated (or incomplete) here. AccessTokenCredentials doesn't provide a way to refresh the token. So you need to use other types of credentials, as explained in the answers below two possible options came feasible to me: OAuth2Credentials and GoogleCredentials (GoogleCredentials extends OAuth2Credentials). Commented Jul 24, 2017 at 23:48

11 Answers 11

25

I use: oauth2client.client.GoogleCredentials

 cred = oauth2client.client.GoogleCredentials(access_token,client_id,client_secret, refresh_token,expires_at,"https://accounts.google.com/o/oauth2/token",some_user_agent) http = cred.authorize(httplib2.Http()) cred.refresh(http) self.gmail_service = discovery.build('gmail', 'v1', credentials=cred) 
Sign up to request clarification or add additional context in comments.

2 Comments

I want to use the refresh_token (saved in a database) to retrieve a new access_token later. If I use oauth2client.client.GoogleCredentials, as you've suggested here, should I save the access_code also along-with the refresh_code in my database for future use?
you can set access_token to None (as you're renewing the token anyway), and expires_at can be None as well
11

You can construct an OAuth2Credentials instance directly like this:

import httplib2 from oauth2client import GOOGLE_REVOKE_URI, GOOGLE_TOKEN_URI, client CLIENT_ID = '<client_id>' CLIENT_SECRET = '<client_secret>' REFRESH_TOKEN = '<refresh_token>' credentials = client.OAuth2Credentials( access_token=None, # set access_token to None since we use a refresh token client_id=CLIENT_ID, client_secret=CLIENT_SECRET, refresh_token=REFRESH_TOKEN, token_expiry=None, token_uri=GOOGLE_TOKEN_URI, user_agent=None, revoke_uri=GOOGLE_REVOKE_URI) credentials.refresh(httplib2.Http()) # refresh the access token (optional) print(credentials.to_json()) http = credentials.authorize(httplib2.Http()) # apply the credentials 

Comments

5

I solved this quite easily (you certainly miss this documentation). This is a snippet of my code that tries to use Picasa API to get all of album from active user:

 http = httplib2.Http(ca_certs=os.environ['REQUESTS_CA_BUNDLE']) try: http = self.oauth.credentials.authorize(http) response, album_list = http.request(Picasa.PHOTOS_URL, 'GET') if response['status'] == '403': self.oauth.credentials.refresh(http) response, album_list = http.request(Picasa.PHOTOS_URL, 'GET') album_list = json.load(StringIO(album_list)) except Exception as ex: Logger.debug('Picasa: error %s' % ex) return {} 

Use the refresh method coming from oauth2client.client.OAuth2Credentials. I think it's even okay to use if response['status'] != '200'. Got to check that!

1 Comment

The link is broken, can you refresh it?
5

In case anyone is looking for the answer for how use a refresh token with google_auth_oauthlib, the following works for me:

flow.oauth2session.refresh_token(flow.client_config['token_uri'], refresh_token=refresh_token, client_id=<MY_CLIENT_ID>, client_secret=flow.client_config['client_secret']) creds = google_auth_oauthlib.helpers.credentials_from_session( flow.oauth2session, flow.client_config) 

I cannot find anywhere where this is documented though.

Comments

4

You can also use the requests library as well:

import google.auth.transport.requests import requests request = google.auth.transport.requests.Request() credentials.refresh(request) 

Here is my sample code on an active project:

acct_creds = { 'token': self.attachment.account.google_drive_access_token, 'refresh_token': self.attachment.account.google_drive_refresh_token, 'client_id': settings.GOOGLE_CLIENT_ID, 'client_secret': settings.GOOGLE_CLIENT_SECRET, 'token_uri': 'https://37947.ngrok.io/authenticate/google/callback/', 'scopes': 'https://www.googleapis.com/auth/drive.appdata https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive.install', } credentials = google.oauth2.credentials.Credentials(**acct_creds) if credentials.valid: print("Credentials valid") else: request = google.auth.transport.requests.Request() credentials.refresh(request) 

google.auth.transport.requests module

Comments

3

I recommend this method.

from oauth2client import client, GOOGLE_TOKEN_URI CLIENT_ID = "client_id" CLIENT_SECRET = "client_secret" REFRESH_TOKEN = "refresh_token" credentials = client.OAuth2Credentials( access_token = None, client_id = CLIENT_ID, client_secret = CLIENT_SECRET, refresh_token = REFRESH_TOKEN, token_expiry = None, token_uri = GOOGLE_TOKEN_URI, token_ id = None, revoke_uri= None) http = credentials.authorize(httplib2.Http()) 

Even if the access token has expired, the credential is still authorize because of the refresh token.

Comments

3

If you are using the 2018 Youtube Python Quickstart demo app using google-auth, you can't use oauth2client's storage.

So here is the correct way of storing the credentials

Here is a partially working solution for google-auth, missing the correct handling of the case where the token expires:

import os import json import os.path import google.oauth2.credentials from google.oauth2.credentials import Credentials from googleapiclient.discovery import build from googleapiclient.errors import HttpError from google_auth_oauthlib.flow import InstalledAppFlow CLIENT_SECRETS_FILE = "client_secret.json" SCOPES = ['https://www.googleapis.com/auth/youtube.force-ssl'] API_SERVICE_NAME = 'youtube' API_VERSION = 'v3' def get_authenticated_service(): if os.path.isfile("credentials.json"): with open("credentials.json", 'r') as f: creds_data = json.load(f) creds = Credentials(creds_data['token']) else: flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES) creds = flow.run_console() creds_data = { 'token': creds.token, 'refresh_token': creds.refresh_token, 'token_uri': creds.token_uri, 'client_id': creds.client_id, 'client_secret': creds.client_secret, 'scopes': creds.scopes } print(creds_data) with open("credentials.json", 'w') as outfile: json.dump(creds_data, outfile) return build(API_SERVICE_NAME, API_VERSION, credentials = creds) def channels_list(service, **kwargs): results = service.channels().list(**kwargs).execute() print('This channel\'s ID is %s. Its title is %s, and it has %s views.' % (results['items'][0]['id'], results['items'][0]['snippet']['title'], results['items'][0]['statistics']['viewCount'])) if __name__ == '__main__': os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' service = get_authenticated_service() channels_list(service, part='snippet,contentDetails,statistics', forUsername='GoogleDevelopers') # or if the above doesn't work channels_list(service, part='snippet,contentDetails,statistics', id='YOUR_YOUTUBE_CHANNEL_ID') 

2 Comments

Thanks this works! I just need it for a simple cli app
Have amended the code because this example may not work on API v3 since channels do not necessarily have a username attached any more. See issuetracker.google.com/issues/35173130?pli=1#comment5
1

If you have a refresh token then you can generate credentials for use by using OAuth2Credentials as below

from googleapiclient.discovery import build import httplib2 from oauth2client import client, GOOGLE_TOKEN_URI client_id = "YOUR_CLIENT_ID" client_secret = "YOUR_CLIENT_SECRET" refresh_token = "YOUR_REFRESH_TOKEN" creds = client.OAuth2Credentials( access_token = None, client_id = client_id, client_secret = client_secret, refresh_token = refresh_token, token_expiry = None, token_uri = GOOGLE_TOKEN_URI, user_agent="pythonclient") creds.refresh(httplib2.Http()) 

I don't know what goes in the user agent but I put a random word in there

Now you can use it to build service object and use google APIs like

service = build("drive", "v3", credentials=creds) 

In case someone wants to generate and use a offline refresh token for use without having to handle the autorization since it's for your testing then use google oauth playground to generate one. Checkout this video for more information.

Comments

0

You could store the entire credentials rather than only the refresh token:

json = credentials.to_json() credentials = Credentials.new_from_json(json) 

Look at the Storage object which does it this way.

5 Comments

how will that help me to gain an access token later on? the access token contained in the credentials expires, while the refresh token doesn't; so I have to get a new access token later.
I believe credentials.authorize(http) will handle refreshing the token automatically on a 401 response.
I got a 403 response, insufficientPermissions Indicates that the user does not have sufficient permissions for the entity specified in the query, which I think it needs an access token to be generated. Any idea? I am working on it currently
I use this, and it seems to be working fine for a refresh token.
I can approve that library automatically refreshes access token when it get 401 (debugged) so answer is correct
0

Wow.. 2 years old question and not a good answer.. No surprise given that Google documentation is crap regarding this.

The correct way to do this is by extending the Storage class oauth2client.client.Storage

An example implementation(using mongodb collection _google_credentials) would be something like:

class Storage(oauth2client.client.Storage): def __init__(self, key): super(Storage, self).__init__() self._key = key def locked_get(self): if not self._key: return None data = _google_credentials.find_one({'_id': self._key}) if not data: return None credentials = oauth2client.client.Credentials.new_from_json(json.dumps(data)) credentials.set_store(self) return credentials def locked_put(self, credentials): data = json.loads(credentials.to_json()) _google_credentials.update_one({'_id': self._key}, {'$set': data}, upsert=True) credentials.set_store(self) def locked_delete(self): bucket.delete(self._key) 

Then when you initially get the credentials after step2_exchange, you need to store them using Storage().put:

e.g:

credentials = flow.step2_exchange(code) Storage(user_id).put(credentials) 

When you need the credentials again, just do:

credentials = Storage(user_id).get() 

1 Comment

The oauth2client is deprecated, google-auth.readthedocs.io/en/latest/… Google recommends you use google-auth google-auth.readthedocs.io/en/latest
0

If you already have a Credentials object then you can refresh it like so:

if refresh: import google_auth_httplib2 # credentials instanceof google.oauth2.credentials.Credentials credentials.refresh(google_auth_httplib2.Request(httplib2.Http())) 

I had created the Credentials object from an old token JSON file like so:

 credentials = google.oauth2.credentials.Credentials( token=token_json['access_token'], refresh_token=token_json['refresh_token'], id_token=token_json['id_token'], token_uri=token_json['token_uri'], client_id=token_json['client_id'], client_secret=token_json['client_secret'], scopes=token_json['scopes']) 

In this way I was able to adapt some old oauth2client code.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.