Объединение обработки OAuth между gdata и более новыми API Google

Я работаю с API контактов Google и API Календаря Google в Python. Первый - это API GData, а второй - API Google API..., поэтому, хотя клиенты доступны, каждый из них покрывается отдельными клиентами - вот GData и Google API.

Проблема, с которой я сталкиваюсь при работе с этими клиентами, заключается в том, что у них обоих есть свой собственный способ работы с OAuth2. Библиотека GData обеспечивает gdata.gauth.token_to_blob(auth_token) а также gdata.gauth.token_from_blob(auth_token) методы для перевода токенов аутентификации в / из строк для хранения в базе данных, в то время как библиотека google-api предоставляет метод в App Engine (платформе, для которой я пишу) для хранения учетных данных OAuth.

Я не вижу четкого способа сохранить одну вещь (будь то токен доступа или учетные данные), доступную для обоих API, но я действительно не хочу, чтобы пользователи проходили аутентификацию дважды. Есть ли способ сделать это, если не использовать клиентские библиотеки Google и писать прямые HTTP-вызовы?

2 ответа

Решение

Я смог получить следующую работу. Он использует oauth2decorator чтобы сделать тяжелую работу, то он использует небольшой класс помощника TokenFromOAuth2Creds применить те же учетные данные к клиенту gdata.

Я должен предвосхитить, что я не эксперт по gdata - и, возможно, есть лучшие способы сделать это - и я не прошел тщательную проверку.

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)

Обратите внимание: если вы не используете декоратор и получили свой объект учетных данных другими способами, вы можете создать тот же предварительно авторизованный объект http:

http = credentials.authorize(httplib2.Http())

Альтернативой использованию gdata является использование http объект (возвращается decorator.http()) напрямую - этот объект автоматически добавит правильные заголовки авторизации для вас - это можно использовать для отправки запросов к любому API, но вам нужно будет обработать обработку запроса и синтаксический анализ XML/JSON самостоятельно:

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])

Документы для httplib2: http://httplib2.googlecode.com/hg/doc/html/libhttplib2.html

Похоже, вам нужно взломать свой путь и переписать GData API, чтобы позволить вам использовать токен из хранилища.

Но даже если вы можете заставить GData вести себя так, как вам хочется, имейте в виду, что токены даются для определенных областей, поэтому вам нужно будет заставить GData API использовать API Календаря Google и наоборот. Хотя я не знаю, можно ли это сделать.