'Google API: getting Credentials from refresh token with oauth2client.client
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)
Solution 1:[1]
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)
Solution 2:[2]
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
Solution 3:[3]
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!
Solution 4:[4]
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.
Solution 5:[5]
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')
Solution 6:[6]
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.
Solution 7:[7]
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)
Solution 8:[8]
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.
Solution 9:[9]
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()
Solution 10:[10]
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.
Solution 11:[11]
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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | leah |
| Solution 2 | |
| Solution 3 | |
| Solution 4 | Neil |
| Solution 5 | Alan B |
| Solution 6 | |
| Solution 7 | |
| Solution 8 | tjsar |
| Solution 9 | |
| Solution 10 | Neil C. Obremski |
| Solution 11 | Monu Yadav |
