Reputation: 10070
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)
Upvotes: 34
Views: 50491
Reputation: 570
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.
Upvotes: 1
Reputation: 11221
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')
Upvotes: 3
Reputation: 1500
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
Upvotes: 4
Reputation: 20244
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.
Upvotes: 0
Reputation: 1938
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.
Upvotes: 5
Reputation: 149716
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
Upvotes: 11
Reputation: 77
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.
Upvotes: 3
Reputation: 561
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()
Upvotes: 0
Reputation: 5147
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!
Upvotes: 5
Reputation: 433
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)
Upvotes: 25
Reputation: 35
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.
Upvotes: 0