Using OAuth2 with service account on gdata in python
Asked Answered
D

2

8

I want to use data.photos.service.PhotosService to push and pull photos from Picasa. I got a service key file XXXXXXXX-privatekey.p12 from Google console and am now trying to authenticate using said key with google.

The documentation for OAUTH2 using appengine has led me to believe that using the following would be of use:

f = file(settings.SITE_ROOT + '/aurora/' + settings.PRIVATE_KEY, 'rb')
key = f.read()
f.close()

credentials = SignedJwtAssertionCredentials(settings.SERVICE_ACCOUNT_NAME, key, scope = 'http://picasaweb.google.com/data https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile')
http = httplib2.Http()
http = credentials.authorize(http)
service = build("oauth2", "v2", http=http)
user_info = None
try:
  user_info = service.userinfo().get().execute()
  # neither of these two methods work
  #gd_client.SetOAuthInputParameters(signature_method = gdata.auth.OAuthSignatureMethod.RSA_SHA1, consumer_key = "asdfasdfasdf.apps.googleusercontent.com", rsa_key = key, two_legged_oauth = True, requestor_id = user_info.get('email'))
  #gd_client.auth_token = gdata.gauth.TwoLeggedOAuthRsaToken(consumer_key = user_info.get('email'), rsa_private_key = key, requestor_id = user_info.get('email'))
except errors.HttpError, e:
  logging.error('An error occurred: %s', e)

user_inf0 = {u'verified_email': True, u'id': u'1234', u'name': u'[email protected]', u'email': u'[email protected]'}

The issue is that either method 1 using SetOAuthInputParameters returns a invalid token, or method 2 returns a 403 restricted.

I am at my wits' end reading through mountains of code that all do regular 3 legged oauth when I really and truly do not want to do it that way. Any ideas/articles I haven't seen yet?

Dortheydorthy answered 15/4, 2013 at 23:26 Comment(0)
M
19

Use gdata.gauth.OAuth2TokenFromCredentials.

auth2token = gdata.gauth.OAuth2TokenFromCredentials(credentials)
gd_client = auth2token.authorize(gd_client)

OAuth2TokenFromCredentials is designed to help you use apiclient and gdata at the same time. Under the covers, it uses the credentials for making sure it has the auth information it needs to perform gdata calls.

Note, if you still get 403, it may be something else entirely. I was using a service account to access a user's data and was getting 403 because I hadn't spec'd the user properly in the SignedJwtAssertionCredentials call.

UPDATE: Here's the basic pattern I used:

from oauth2client.client import SignedJwtAssertionCredentials
credentials = SignedJwtAssertionCredentials(
    "[email protected]",
    open("keyfile").read(),
    scope=(
        "https://www.googleapis.com/auth/drive",
        "https://spreadsheets.google.com/feeds",
        "https://docs.google.com/feeds"
    ), # For example.
    sub="[email protected]"
)
http = httplib2.Http()
http = credentials.authorize(http) # Not needed? See comment below.
auth2token = gdata.gauth.OAuth2TokenFromCredentials(credentials)
gd_client = gdata.photos.service.PhotosService() # For example.
gd_client = auth2token.authorize(gd_client)
Mansell answered 22/8, 2013 at 15:1 Comment(9)
how did you specify the user in the SignedJwtAssertionCredentials call ?Rabbinism
I keep getting HTTP 400 with this, I am using the spreadsheet APIRabbinism
Thank you for this pattern! I had to remove the sub parameter in the SignedJwtAssertionCredentials call, because I got AccessTokenRefreshError: access denied. Works fine now! #26925625Cockchafer
the credentials.authorize(http) line is dead code. It's the rest of the stuff that seems to matter.Detachment
@Karra, very well may be. I didn't confirm all of the nuts and bolts once I got it working.Mansell
Gdata docs explain bridging gdata-python-client and google-api-python-client github.com/google/gdata-python-client/blob/…Manymanya
@DavidK.Hess I wish I could vote your answer up again for providing the pattern.Dortheydorthy
Which import do you need for SignedJwtAssertionCredentials?Amidst
@PetrusTheron, added the missing import.Mansell
L
0

If you are using MFA on your google account, you need to use the consent screen authentication method. With Picassa API, it does not work as is, as the request API is slightly different.

import gdata.gauth
import os
import pickle
import gdata.photos.service

clientid='xxx'  # https://console.developers.google.com/apis/credentials
clientsecret='xxx'
Scope='https://picasaweb.google.com/data/'
User_agent='myself'

def GetAuthToken():
    if os.path.exists(".token"):
        with open(".token") as f:
            token = pickle.load(f)
    else:
        token = gdata.gauth.OAuth2Token(client_id=clientid,client_secret=clientsecret,scope=Scope,user_agent=User_agent)
        print token.generate_authorize_url(redirect_uri='urn:ietf:wg:oauth:2.0:oob')
        code = raw_input('What is the verification code? ').strip()
        token.get_access_token(code)
        with open(".token", 'w') as f:
            pickle.dump(token, f)
    return token


token = GetAuthToken()

gd_client = gdata.photos.service.PhotosService()
old_request = gd_client.request


def request(operation, url, data=None, headers=None):
    headers = headers or {}
    headers['Authorization'] = 'Bearer ' + token.access_token
    return old_request(operation, url, data=data, headers=headers)


gd_client.request = request
photos = gd_client.GetUserFeed(kind='photo', limit='10')
for photo in photos.entry:
    print 'Recently added photo title:', photo.title.text
Lancewood answered 8/9, 2017 at 20:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.