0

My app is registered in Google and I have enabled the contacts API.

In the first view I am getting the access token and I am redirecting the user to the Google confirmation page where he will be prompted to give access to his contacts:

SCOPE = 'https://www.google.com/m8/feeds/'
CLIENT_ID = 'xxxxxxxx'
CLIENT_SECRET = 'xxxxxxxxx'
APPLICATION= 'example.com'
USER_AGENT = 'dummy-sample'
APPLICATION_REDIRECT_URI = 'http://example.com/oauth2callback/'

def import_contacts(request):

    auth_token = gdata.gauth.OAuth2Token(
        client_id=CLIENT_ID, client_secret=CLIENT_SECRET,
        scope=SCOPE, user_agent=USER_AGENT)

    authorize_url = auth_token.generate_authorize_url(
        redirect_uri=APPLICATION_REDIRECT_URI)

    return redirect(authorize_url)

If the user clicks Allow, then Google redirects to my handler which shall retrieve the contacts:

def oauth2callback(request):
    code = request.GET.get('code', '')
    redirect_url = 'http://example.com/oauth2callback?code=%s' % code
    url = atom.http_core.ParseUri(redirect_url)

    auth_token.get_access_token(url.query)

    client = gdata.contacts.service.ContactsService(source=APPLICATION)
    auth_token.authorize(client)
    feed = client.GetContactsFeed()

As you can see, my problem is how to get the auth_token object in the second view, because this code is failing on the line auth_token.get_access_token(url.query). I have tried without success multiple options like putting the object in the session but it is not serializable. I tried also gdata.gauth.token_to_blob(auth_token) but then I can retrieve only the token string and not the object. Working with gdata.gauth.ae_save() and ae_load() seem to require in some way Google App Engine.

The alternative approach that I see in order to get the contacts is to request them directly in the first Django view with the access token, instead exchanging the token with the code:

r = requests.get('https://www.google.com/m8/feeds/contacts/default/full?access_token=%s&alt=json&max-results=1000&start-index=1' % (self.access_token))

But this is not redirecting the users to the google page so that they can give explicitly their approval. Instead, it fetches the contacts directly using the token as credentials. Is this a common practice? What do you think? I think that the first approach is the preferred one, but first I have to manage to get the auth_token object..

Cœur
  • 37,241
  • 25
  • 195
  • 267
katericata
  • 1,008
  • 3
  • 14
  • 33
  • I don't really understand what you're doing here at all. You're constructing a URL from a string yourself, making it into a ParseUri object, then passing the query element to `get_access_token`. Why? The query element is just `code=`, which you could have constructed directly. However if you want more help you'll need to link to the exact client library you're using, as Google has several but your code doesn't seem to match up to any of them. – Daniel Roseman Oct 20 '16 at 18:09
  • But actually I've just realised what you're asking. I still don't know why you're doing any of this rather than following the flow as described the Google developer docs, but the OAuth2Token object is not in any way related to the current request - it can easily either be re-instantiated in the callback function, or instantiated once at module level and used in each function. – Daniel Roseman Oct 20 '16 at 18:48
  • Basically I was trying to follow this example: http://stackoverflow.com/questions/10188768/google-contacts-import-using-oauth2-0/14161012#comment67508588_14161012. I tried to follow the official reference here https://developers.google.com/google-apps/contacts/v3/ but I was not able to build the complete flow. – katericata Oct 20 '16 at 19:01
  • Here are the social packages that I use in the project: `django-allauth (0.25.2)` `django-google-contacts (0.1)` `gdata (2.0.18)` `oauthlib (1.1.1)` requests-oauthlib (0.6.1). Oddly enough but I am not using `gdata-python-client` which was mentioned in the tutorials. May be therefore I get the issue? – katericata Oct 20 '16 at 19:11
  • Did you try using the oauth flow for Python indicated in the [oauth2client library for Python](https://developers.google.com/api-client-library/python/guide/aaa_oauth)? – ReyAnthonyRenacia Oct 22 '16 at 12:59
  • @noogui, I have tried but I got OAuth2AccessTokenError because again I was not able to serialize the token object. I think I am still unable to get the entire picture how to reuse the same object in the second view. However, I managed to solve my problem by putting the object in a session variable, I will post the solution down here. – katericata Oct 24 '16 at 18:07

1 Answers1

0

Finally I was able to serialize the object and put it in the session, which is not a secure way to go but at least it will point me to the right direction so that I can continue with my business logic related with the social apps.

import gdata.contacts.client

def import_contacts(request):

    auth_token = gdata.gauth.OAuth2Token(
        client_id=CLIENT_ID, client_secret=CLIENT_SECRET,
        scope=SCOPE, user_agent=USER_AGENT)

    authorize_url = auth_token.generate_authorize_url(
        redirect_uri=APPLICATION_REDIRECT_URI)
    # Put the object in the sesstion
    request.session['auth_token'] = gdata.gauth.token_to_blob(auth_token)

    return redirect(authorize_url)

def oauth2callback(request):
    code = request.GET.get('code', '')
    redirect_url = 'http://myapp.com/oauth2callback?code=%s' % code
    url = atom.http_core.ParseUri(redirect_url)
    # Retrieve the object from the session
    auth_token = gdata.gauth.token_from_blob(request.session['auth_token'])
    # Here is the tricky part: we need to add the redirect_uri to the object in addition
    auth_token.redirect_uri = APPLICATION_REDIRECT_URI

    # And this was my problem in my question above. Now I have the object in the handler view and can use it to retrieve the contacts.
    auth_token.get_access_token(url.query)
    # The second change I did was to create a ContactsClient instead of ContactsService
    client = gdata.contacts.client.ContactsClient(source=APPLICATION)
    auth_token.authorize(client)
    feed = client.GetContacts()

    all_emails = []
    for i, entry in enumerate(feed.entry):
        # Loop and fill the list with emails here
        ...

    return render_to_response('xxx/import_contacts.html', {'all_emails': all_emails},
                          context_instance=RequestContext(request))
katericata
  • 1,008
  • 3
  • 14
  • 33