Blog

August 15, 2010
Categories:

google app engine service login

So I’m working on an app during my sabbatical that has an iPad component and an online Google App Engine component. The Google App Engine part is half web based and half web service based. Of course this means that the local client part has to be able to authenticate itself to the Google App Engine before it can communicate and do useful stuff. Finding good reliable examples of how to do this is surprisingly hard. For the Objective C code I’m working on I found a nice set of classes that do the trick for you here: On Github For Python I found some example code on stackoverflow. However it was not really in a reusable form.

The basic outline of what you have to do is as follows:

1. Login to https://www.google.com/accounts/ClientLogin This will give you an auth token.
2. Use the token you gained in step 1 to login to your Google App engine application or service. When you have successfully logged in to your service google will set an ACSID cookie for you to use when you make subsequent requests to your service. This prevents you from having to login each time you make a web service request.

I’ve taken some ideas from both places mentioned above and have created a Python class for logging in and accessing app engine services from Python. To use this module you just need to import it and create a GoogleAppEngineLogin object. Once the object is created you can use the open method on the object to access further services. The open method is just a convenience wrapper around urllib2.urlopen but it also makes sure that your cookie has not expired before it makes a request. If you have comments or suggestions for how to improve the code please let me know via email or leave a comment.

The code is reproduced below, but you can also just download the file from git clone git@gist.github.com:36b1c45ed39298178907.git


import getpass
import urllib
import urllib2
import cookielib


class GoogleAppEngineLogin(object):
“””
Logging in to an App Engine Account (when you use google users) is
a two step process: First you must login to Google generally. This
gets you an auth token. The auth token is used as part of a
request to login to your app/service During the login process for
your app/service the server sets a cookie with the name of ACSID,
it is this cookie and its value that serves as the authentication
token for your own service/app. So, for future requests you need
to give the server the cookie as part of your request. Handling
cookies can be a bit tricky if you haven’t had some experience with
it but luckily Python’s cookielib module makes it all pretty
automatic.

This class takes care of the whole login process for you, and then
gives you a simple helper to access the URLs for your service.
The helper function makes sure the cookie is still valid and
passes on the request along with the cookie. Technically you
would not even need to use the helper function, you could use
urllib2 directly to access your service but this seems a bit
neater to me.

Some of this code was inspired by and lifted from an example on
stackoverflow.com, but that was all in-line code my contribution
is to add some error handling and encapsulate the whole thing
inside a class to make it easier to include in my/your own
programs. Here’s a link to the original thread on stackoverflow
http://stackoverflow.com/questions/101742/how-do-you-access-an-authenticated-google-app-engine-service-from-a-non-web-pyt

“””

def init(self, user_email, user_pw, uri, source):
“””
Create a Google App Engine Object.
Arguments:
- user_email: your google username
- user_pw: your google password
- uri: The url of your google app engine service
- source: The unique name of your google app engine service
“””
self._user_email = user_email
self._user_pw = user_pw
self._uri = uri
self._source = source
self._authtoken = None
self._auth_cookie = None

if not self.google_client_login():
raise RuntimeError(“Could not login to Google”)

if not self.app_engine_login():
raise RuntimeError(“Could not login to your application”)


def google_client_login(self):
#
# get an AuthToken from Google accounts
#
auth_uri = ‘https://www.google.com/accounts/ClientLogin'
authreq_data = urllib.urlencode({ “Email”: self._user_email,
“Passwd”: self._user_pw,
“service”: “ah”,
“source”: self._source,
“accountType”: “HOSTED_OR_GOOGLE” })
auth_req = urllib2.Request(auth_uri, data=authreq_data)
try:
auth_resp = urllib2.urlopen(auth_req)
auth_resp_body = auth_resp.read()
except:
return False
# auth response includes several fields - we’re interested in
# the bit after Auth=
auth_resp_dict = dict(x.split(“=”)
for x in auth_resp_body.split(”\n”) if x)
try:
self._authtoken = auth_resp_dict[“Auth”]
except:
return False

return True

def app_engine_login(self):
#
# Get a cookie
# we use a cookie to authenticate with Google App Engine
# by registering a cookie handler here, this will automatically store the
# cookie returned when we use urllib2 to open
# http://www.google.com/accounts/ClientLogin
self._cookiejar = cookielib.LWPCookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self._cookiejar))
urllib2.install_opener(opener)


serv_args = {}
serv_args[‘continue’] = self._uri
serv_args[‘auth’] = self._authtoken

full_serv_uri = “%s/_ah/login?%s” % (self._uri,urllib.urlencode(serv_args))

serv_req = urllib2.Request(full_serv_uri)
serv_resp = urllib2.urlopen(serv_req)
serv_resp_body = serv_resp.read()


for i, c in enumerate(self._cookiejar):
if c.name == ‘ACSID’:
self._auth_cookie = c
return True

return False

def open(self,url,data=None):
“””
url should be a properly encoded url ready to go. data is
optional and should be used to provide parameters to pass
along with the URL when you want to use POST instead of GET.
If you provide data it must be properly encoded just as if you
were calling urlopen directly yourself.
“””
if self._auth_cookie.is_expired():
if not self.google_client_login() or not self.app_engine_login():
raise RuntimeError(“Cannot get proper authorization for this request”)

serv_req = urllib2.Request(url,data)
return urllib2.urlopen(serv_req)


if name == “main“:
user = raw_input(“User: “)
pw = getpass.getpass(“Password: “)
service_url = “http://myapp.appspot.com"
service_name = “myapp”
gae = GoogleAppEngineLogin(user,pw,service_url,service_name)
h = gae.open(“http://myapp.appspot.com/my/service")
print h.read()