0
votes

I'm trying to setup endpoints api (with google app engine, python), but I'm having some trouble getting user profile info. API is working, I can create entities through API Explorer on my localhost.

My goal is to allow user to register for my app by providing just an email, and authorizing the app to get the reset of the info from their profile. I have this endpoints method:

@User.method(http_method="POST",
             auth_level=endpoints.AUTH_LEVEL.REQUIRED,
             allowed_client_ids=[
               endpoints.API_EXPLORER_CLIENT_ID
             ],
             scopes=[
               'https://www.googleapis.com/auth/userinfo.email',
               'https://www.googleapis.com/auth/userinfo.profile',
               'https://www.googleapis.com/auth/plus.me',
             ],
             user_required=True,
             request_fields=('email',),
             response_fields=('id',),
             name="register",
             path="users")
def UserRegister(self, instance):
    logging.info(os.getenv( 'HTTP_AUTHORIZATION' ))
    # 'Beared __TOKEN__'
    logging.info(endpoints.users_id_token._get_token(None))
    # '__TOKEN__'
    instance.put()
    return instance

This works fine, I receive authorization token and user is created in datastore, but I can't figure out how to get the profile info. If I enter the token in OAuth2 API (through API Explorer):

POST https://www.googleapis.com/oauth2/v2/tokeninfo?access_token=__TOKEN__

I get token info with some data I need { "user_id": "__ID__", "verified_email": true, ...}, and if I use user_id in +API:

GET https://www.googleapis.com/plus/v1/people/__ID__

I can get the rest of the data I need (name, image, etc).

What do I need to do to achieve this in my UserRegister() method? I'd prefer to return just entity ID and do the rest of registration asynchronously, but that's another issue, I'll figure it out (; Just need some guidance how to call other endpoints from my code...

EDIT:

I've managed to figure out how to call other APIs (code on Gist), now only have one issue with Plus API:

I did some queries and eventually got anonymous quota error. Then I added key parameter and set it to WEB_CLIENT_ID or SERVICE_ACCOUNT:

  • WEB_CLIENT_ID is OAuth2 Client ID (type: Web Application) from console.developers.google.com/apis/credentials,
  • SERVICE_ACCOUNT is default App Engine service account - [email protected]...

and now I'm getting following error:

HttpError: <HttpError 400 when requesting https://www.googleapis.com/plus/v1/people/__VALID_USER_ID__?key=__WEB_CLIENT_ID__or__SERVICE_ACCOUNT__&alt=json returned "Bad Request">

When I use +API explorer I get results as expected:

REQUEST:

https://www.googleapis.com/plus/v1/people/__VALID_USER_ID__?key={YOUR_API_KEY}

RESPONSE:

200 OK + json data for user...

Anyone knows why is this happening?

1
Please remove the "SOLVED" text from your question's headline. It's completely unnecessary. Accept an answer - you can accept your own answer - and everyone will see that the question has an accepted solution.konqi

1 Answers

0
votes

Why am I getting BadRequest response?

Problem with BadRequest was that I didn't send authorization token... I did try to send it as access_token, but seams like +api docs are outdated - it should be oauth_token. When I included this parameter issue was resolved:

build('plus', 'v1').people().get(userId=user_id, key=SERVICE_ACCOUNT, oauth_token=token).execute()

HINT: Use http://localhost:8001/_ah/api/discovery/v1/apis/, and discoveryRestUrl property it has to see real properties of your API - this is where I found the answer.

oauth_token can be obtained like this:

token = os.getenv('HTTP_AUTHORIZATION').split(" ")[1]
# or like in my question:
token = endpoints.users_id_token._get_token(None)

I'd suggest HTTP_AUTHORIZATION variable, because users_id_token docs state that it's a:

Utility library for reading user information from an id_token.

This is an experimental library that can temporarily be used to extract a user from an id_token. The functionality provided by this library will be provided elsewhere in the future.

How to call other API Endpoints?

This is also an answer to my first question:

from googleapiclient.discovery import build

service = build('plus', 'v1')
request = service.people().get(userId=user_id, key=SERVICE_ACCOUNT, oauth_token=token)
response = request.execute()

data = dict(self.response.POST)

Code that worked for me is here.

NOTE: WEB_CLIENT_ID obtained from https://console.developers.google.com/apis/credentials (OAuth2 Client ID of type Web Application) will NOT work in this case. I had to use SERVICE_ACCOUNT - I didn't try to generate one through console, default service account I got from App Engine worked fine.

...things are much clearer now that I got this working. Hope it will help someone else (;