I'm working with the Google Contacts API and Google Calendar API in Python. The former is a GData API and the latter is a Google API... API, so while clients are available, they're each covered by separate clients -- here's GData, and Google API.
The problem I'm running into working with these clients is that they both have their own way of dealing with OAuth2. The GData library provides gdata.gauth.token_to_blob(auth_token)
and gdata.gauth.token_from_blob(auth_token)
methods to translate auth tokens to/from strings to store in a database, while the google-api library provides a method on App Engine (the platform I'm writing for) to store the OAuth credentials.
I don't see a clear way to store a single thing (whether it be an access token or credentials) accessible to both APIs, but I really don't want users to have to authenticate twice. Is there a way to accomplish this, short of ditching Google's client libraries and writing straight HTTP calls?
I was able to get the following working. It uses the oauth2decorator
to do the heavy lifting, then it uses a small helper class TokenFromOAuth2Creds
to apply those same credentials to the gdata client.
I should preface that I'm no gdata expert - and there may be better ways of doing this - and I haven't thoroughly tested.
import webapp2
import httplib2
from oauth2client.appengine import oauth2decorator_from_clientsecrets
from apiclient.discovery import build
import gdata.contacts.client
decorator = oauth2decorator_from_clientsecrets(
"client_secrets.json",
scope=["https://www.google.com/m8/feeds", "https://www.googleapis.com/auth/calendar.readonly"]
)
# Helper class to add headers to gdata
class TokenFromOAuth2Creds:
def __init__(self, creds):
self.creds = creds
def modify_request(self, req):
if self.creds.access_token_expired or not self.creds.access_token:
self.creds.refresh(httplib2.Http())
self.creds.apply(req.headers)
class MainHandler(webapp2.RequestHandler):
@decorator.oauth_required
def get(self):
# This object is all we need for google-api-python-client access
http = decorator.http()
# Create a gdata client
gd_client = gdata.contacts.client.ContactsClient(source='<var>YOUR_APPLICATION_NAME</var>')
# And tell it to use the same credentials
gd_client.auth_token = TokenFromOAuth2Creds(decorator.get_credentials())
# Use Contacts API with gdata library
feed = gd_client.GetContacts()
for i, entry in enumerate(feed.entry):
self.response.write('\n%s %s' % (i+1, entry.name.full_name.text if entry.name else ''))
# Use Calendar API with google-api-python-client
service = build("calendar", "v3")
result = service.calendarList().list().execute(http=http)
self.response.write(repr(result))
app = webapp2.WSGIApplication([
("/", MainHandler),
(decorator.callback_path, decorator.callback_handler()),
], debug=True)
Note, if you're not using the decorator, and have gotten your credentials object by other means, you can create the same pre-authorized http object by:
http = credentials.authorize(httplib2.Http())
An alternative to using gdata is to use the http
object (returned by decorator.http()
) directly - this object will automatically add the right authorization headers for you - this can be used to make requests to either API, but you'll need to handle crafting the request and parsing the XML/JSON yourself:
class MainHandler(webapp2.RequestHandler):
@decorator.oauth_required
def get(self):
http = decorator.http()
self.response.write(http.request('https://www.google.com/m8/feeds/contacts/default/full')[1])
self.response.write(http.request('https://www.googleapis.com/calendar/v3/users/me/calendarList')[1])
Docs for httplib2: http://httplib2.googlecode.com/hg/doc/html/libhttplib2.html#http-objects
It seems that you'll need to hack your way and rewrite the GData API to let you use a token from a Storage.
But even if you are able to make GData behave as you want keep in mind that tokens are given for certain scope(s), so you will need to force the GData API to use the Google Calendar API and vice versa. I don't know if it can be done, though.
来源:https://stackoverflow.com/questions/19991584/unifying-oauth-handling-between-gdata-and-newer-google-apis