Thursday, December 10, 2009

Programmatically authenticating to Google App Engine with Python

Even before joining Google, I was already a fan of two of its services: Google App Engine and Google Apps for Domains. Apps itself brings a lot of value and combined with app engine, it's especially powerful for small business and start-ups. Apps provides the most essential IT pieces for a business (email, office suite, calendaring) and Google App Engine (GAE) allows the business to build their own software on Google's infrastructure without the heavy initial investment in hardware and IT infrastructure. This post focuses on just GAE specifically how you can programmatically access app engine from a python script.

The code to authenticate against the production instance of GAE is actually fairly well documented by Google on the GAE site. The issue I ran into is that before I push my code live, I wanted to test it on a development instance. GAE makes setting up a development server easy and comes with its own web server and datastore (database), but the one area where the development environment is different from production is how authentication is handled. The development server doesn't do real authentication. It just sets the right cookie that simulate that you've already logged in. I wanted to write my code with minimal differences between how it calls production and how it calls the localhost. Authentication to your application on production means that you need to get a token from the Google servers, but this doesn't exists on the dev environment. Looking at how the developer instance handled the login screen, I was able to do the following:



#!/usr/bin/python

import cookielib
import urllib
import urllib2

# Setup to be able to get the needed cookies that GAE returns
cookiejar = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))
urllib2.install_opener(opener)

# Set 'prod' based on how your system determines whether to connect to prod or localhost.
if prod:
# This is the setup to construct the login URL for authentication on prod.
authreq_data = urllib.urlencode({'Email': '',
'Passwd': '',
'service': 'ah',
'source': '',
'accountType': 'HOSTED_OR_GOOGLE'})

# Authentication server
token_uri = 'https://www.google.com/accounts/ClientLogin'

# This is where you want to go after log in. Replace:
target_uri = 'http://auth_example.appspot.com/home'
## Get an AuthToken from Google Accounts
auth_req = urllib2.Request(token_uri, data=authreq_data)
auth_resp = opener.open(auth_req)
auth_resp_body = auth_resp.read()
auth_resp_dict = dict(x.split('=')
for x in auth_resp_body.split('\n') if x)
authtoken = auth_resp_dict['Auth']

authreq_data = urllib.urlencode({'continue': target_uri,
'auth': authtoken})
login_uri = ('http:///_ah/login?%s'
% authreq_data)
else: # authenticate against the local dev server
target_uri = 'http://localhost:8080/home'
authreq_data = urllib.urlencode({'email': 'test@example.com',
'continue': target_uri,
'action': 'Login'})
login_uri = ('http://localhost:8080/_ah/login?%s' % authreq_data)

# Okay, we're done at this point with the difference. From this point everything else
# should be the same for either prod or dev.

# Do the actual login and getting cookies.
serv_req = urllib2.Request(login_uri)
opener.open(serv_req)

# Rest of code here.


That all there is to it!

2 comments:

  1. I can really just use "if prod:" in my code, and that is truly only executed on production?

    I was just looking at a similar example. Good stuff.

    ReplyDelete
  2. You would set the value of prod based on your use case. For me, I pass in a parameter that tells the script whether to authenticate against production GAE instance or the localhost instance. I'll clarify the script to make that more obvious.

    ReplyDelete