Unifying OAuth handling between gdata and newer Google APIs

寵の児 提交于 2019-12-01 12:58:32

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!