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'asdfasdfasdf@developer.gserviceaccount.com', u'email': u'asdfasdfasdf@developer.gserviceaccount.com'}

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?

poolie
  • 9,289
  • 1
  • 47
  • 74
Nielsvh
  • 1,151
  • 1
  • 18
  • 31

2 Answers2

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(
    "XXXXXXXXXXX@developer.gserviceaccount.com",
    open("keyfile").read(),
    scope=(
        "https://www.googleapis.com/auth/drive",
        "https://spreadsheets.google.com/feeds",
        "https://docs.google.com/feeds"
    ), # For example.
    sub="user@gmail.com"
)
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)
David K. Hess
  • 16,632
  • 2
  • 49
  • 73
  • how did you specify the user in the `SignedJwtAssertionCredentials` call ? – Gautam Nov 22 '13 at 09:47
  • I keep getting HTTP 400 with this, I am using the spreadsheet API – Gautam Nov 25 '13 at 11:12
  • 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! http://stackoverflow.com/questions/26925125/accesstokenrefresherror-google-spreadsheet-api-with-oauth-2-0-service-account-o – Ani Nov 14 '14 at 13:56
  • the credentials.authorize(http) line is dead code. It's the rest of the stuff that seems to matter. – Karra Jun 05 '15 at 12:19
  • @Karra, very well may be. I didn't confirm all of the nuts and bolts once I got it working. – David K. Hess Jun 05 '15 at 15:11
  • 1
    Gdata docs explain bridging gdata-python-client and google-api-python-client https://github.com/google/gdata-python-client/blob/437892ce10d0325daaaf9e91bf95c3e1c9fdabbd/src/gdata/gauth.py#L1418 – ckhan Dec 04 '15 at 10:05
  • 2
    @DavidK.Hess I wish I could vote your answer up again for providing the pattern. – Nielsvh Dec 04 '15 at 16:02
  • 1
    Which import do you need for `SignedJwtAssertionCredentials`? – Petrus Theron Nov 14 '16 at 17:58
  • @PetrusTheron, added the missing import. – David K. Hess Jan 14 '17 at 14:33
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
tardyp
  • 1,142
  • 11
  • 9