Authenticating in GAE
Having successfully created a desktop application in IronPython, I wanted to go a step further – integrate desktop and web environment. Data can be captured in desktop and passed on to web; and analysis can be done on the web.
To start with it, I need to authenticate the incoming request. Hmm…when I started reading about authentication I read so many jargons that scared me – openid, openauth, authsub, basicauth. It took me a while to understand the basics of each (still I don’t claim that I’ve understood them; probably whatever I’ve understood is wrong too)
To some extent, I understood ‘Basic Authentication’ and this is how I went about implementing it.
First of all, I am going with Google Account Authentication, assuming all the users will have a Google account; getting one is not a big deal.
IronPython Desktop Application will pass userid, password to AppEngine Application, which in turn will validate with Google Accounts Authentication service. If authenticated, AppEngine application will proceed; else it will pass the error back to the desktop application.
Having discussed the concepts, here are the code snippets. I’m sharing with a hope that it will be useful to someone; or if this is wrong, someone will raise an alarm; or if there is a better way to do it, someone will comment.
AppEngine
Here the expectation is that the incoming request will have a header (‘HTTP_AUTHORIZATION’) with userid and password in Base64 format.
import urllib
import base64
from google.appengine.api import urlfetch
from django.http import HttpResponse
def authenticate(request):
response_vals={}
resp = HttpResponse()
try:
(method, encoded) = request.META['HTTP_AUTHORIZATION'].split()
response_vals['method'] = method
response_vals['encoded'] = encoded
if method.lower() == 'basic':
(username, password) = base64.b64decode(encoded).split(':')
request_body = urllib.urlencode({'Email': username,
'Passwd': password,
'accountType': 'HOSTED_OR_GOOGLE',
'service': 'ah',
'source': 'test'})
auth_response = urlfetch.fetch('https://www.google.com/accounts/ClientLogin',
method=urlfetch.POST,
headers={'Content-type':'application/x-www-form-urlencoded',
'Content-Length':
str(len(request_body))},
payload=request_body)
resp.status_code = auth_response.status_code
except:
resp.status_code = 401
return resp
return resp
Desktop – Python
Encode the user id, password and call the particular URL (/auth?). And handle the response.
import urllib2
import base64
url = 'http://localhost:8000/api/auth/'
username = 'mysecs@gmail.com'
password = 'mysecspwd'
USER_AGENT = 'mySecsWndClient'
request = urllib2.Request(url)
base64string = base64.encodestring('%s:%s' % (username,password))
authheader = "Basic %s" % base64string
request.add_header('USER_AGENT', USER_AGENT)
request.add_header('AUTHORIZATION', authheader)
try:
response = urllib2.urlopen(request).read()
print response
except urllib2.HTTPError, e:
print e.code
print e.read()
except urllib2.URLError, e:
if hasattr(e, 'reason'):
print e.reason
elif hasattr(e,'code'):
print e.code
Desktop – IronPython
Since I’ve written mySecs in IronPython, I’ve to translate the above into IronPython. As ipy is a relatively new entrant, there is not much of documentation on how to do it. Here is what I’ve done.
from System.IO import StreamReader
from System.Net import HttpWebRequest, NetworkCredential, WebException
url = 'http://localhost:8000/api/auth/'
username = 'mysecs@gmail.com'
password = 'mysecspwd'
USER_AGENT = 'mySecsWndClient'
req = HttpWebRequest.Create(url)
req.UserAgent = USER_AGENT
auth_string = '%s:%s' % (username,password)
authheader = "Basic %s" % System.Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(auth_string))
req.Headers.Add('AUTHORIZATION', authheader)
try:
rsp = req.GetResponse()
response = StreamReader(rsp.GetResponseStream()).ReadToEnd()
print response
rsp.Close()
except (Exception, WebException), e:
print 'Error: %s' % e
Applies to: GAE 1.1.1; Django 0.96 with helper; IronPython 1.1
Reference:
AuthSub Authentication for Web Applications
Basic Authentication – Authentication with Python
You don’t want to send the user credentials using the header method because appengine doesn’t support SSL, so they are sent unencrypted across the wire.
Your desktop app can communicate directly with the Google accounts auth service:
http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html
Joel: thanks for your comment. Agreed that the credentials are passed in plain text. Question: In the webapp, how will I know that the request is from an authenticated user? thanks.
If you want the client app to make authenticated requests to your appengine site, you can use the token obtained from the auth service to generate the auth cookie. There is a good overview and some sample code on StackOverflow:
http://stackoverflow.com/questions/101742/how-do-you-access-an-authenticated-google-app-engine-service-from-a-non-web-pyt
In appengine you can use the Users API to get the current authenticated user for a request:
http://code.google.com/appengine/docs/gettingstarted/usingusers.html
Thanks Joel. I’ll have a look into these links.
Joel: I got it working. I’ve shared the code – http://code.google.com/p/mysecs/source/browse/trunk/WndClient/mysecs/googleHelper.py