finally{}

all will be well finally

By

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.

image

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.

from google.appengine.api import users
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 urllib
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.

import System
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

Related Posts:

5 Responses to Authenticating in GAE

  1. Joel says:

    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

  2. Joseph Jude says:

    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.

  3. Joel says:

    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

  4. Joseph Jude says:

    Thanks Joel. I’ll have a look into these links.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>