Monday, November 17, 2008

Writing a Quick and Worthy Mobility Mashup

While working on the registration forms for our Open ID service (openid.ringful.com), we wanted to implement a process that prevents malicious bulk registrations. A captcha widget was one option, but we didn't like it much, because it does not prevent people with time (or mechanical turks) to register many accounts. Captcha is also difficult for legitimate users. Sometimes it takes several retries to guess what's on a captcha image.
After a little brainstorming we decided to implement a simpe alternative, which is straightforward to implement and is also easy on the end user. The idea is to use a person's phone as a way to prove identity. A web wigdet would ask the user to enter their phone number and choose whether they prefer a text message or a voice call to deliver a secret code that the user will then enter on the registration form. The widget could be used not only for registration, but also for two factor authentication on login. The user has to know the password and has have the physical phone handy at the time of login.

We called the new service Ringful ID Check. It is now available for public use at http://idcheck.ringful.com. A demo web widget is available for instant trial.

As we will see the demo widget is a simple mash up of Web 2.0 AJAX code, a small python script and the Ringful REST API. We will walk through the steps it took to build it. The end result is shown below:



Here is the outline of the process:
  1. Obtain Ringful Application Developer account
  2. Obtain Google App Engine account
  3. Write a Python script to serve the ID Check widget
  4. Write an HTML web form with a little AJAX code for coolness
  5. Wire the python code with the Ringful idcheck API.

Now let's look into detail at each of the steps.

Obtaining Ringful Application Developer account


It takes a few easy steps to obtain a Ringful Developer account.

Start by registering a Ringful user account at http://openid.ringful.com.

Next, go to the ringful Developer Zone site http://devzone.ringful.com. It is accessible from a link at the top of the home page:
Once at the DevZone home page, click on Login. This will take you to our single sign-on Open ID server. After successful login, you will be redirected back to the dev zone site. Now you should see your newly created Dev Zone account. It would look like this:




You will notice a complimentary $5 is added to your account. This is our gesture to you during the open Beta testing period. Thank you for joining!

There is some important information on your Developer Account page that you should understand well in order to use the API properly. In particular the following three items are crucial:
  • Application Key is a hexadecimal string which uniquely identifies your application developer account. You will need it in each invocation to the ringful API. All calls will start with this prefix: http://devzone.ringful.com//apikey/1234, where 1234 will be replaced with your actual application key.
  • Signature Key is a very important hexadecimal string that you should keep to yourself and possibly a small circle of trusted developers on your team. Anyone who has this key will be able to use the Ringful API on your behalf... And you will pay the bill. This key is used to sign each API invocation. It is the last parameter on a HTTP GET URL and is an sha1 function of the query string without the sig parameter. For example in:
http://devzone.ringful.com/SomeAPIMethod/apikey/1234?param1=abcd&param2=efgh&seq=67&sig=26a4bc369fc884e29f43d2075d7199a853cb8a5c
The signature is calculated as follows sig=26a4bc369fc884e29f43d2075d7199a853cb8a5c=hexadec(sha1('param1=abcd&param2=efgh&seq=67' + signature_key))
To be more specific, the server executes signature verification code algorithmically equivalent to the following python snippet:
sig =
http_request.get('sig')
qs = http_request.query_string

unsigned_qs = qs.split('&sig',1)[0]
hash = hashlib.sha1(unsigned_qs + signature_key)
hashstr = hash.hexdigest()
if hashstr != sig:
logging.debug("Invalid signature parameter. API request rejected.")
Notice that the sig parameter is expected to be the last one in the HTTP GET URL.
  • seq is is a monotonously increasing positive integer that should be included in each API request. It is your responsibility to ensure that each consequitive API call from your application has a new seq value that is greater than any previous value. The recommendet way to implement a seq value generator is to use system time with microseconds granularity.

There rest of the information on your Ringful DevZone home page is self explanatory. In case you have any questions, feel free to send them to support at ringful.com.

Obtaining Google App Engine Account


There are many ways to host a web widget, but Google App Engine is probably one of the least expensive (FREE) and easiest to get going for many web developers.
We will just point out to the GAE home page, which has great instructions how to setup an account and get started: http://code.google.com/appengine/.

Writing the server side script


We need to have a server side script with the following functions:
  1. Host the HTML and JavaScript code for the web widget
  2. Process form post from the widget with user phone number and kind of pass code delivery
  3. Generate pass code and deliver it to user phone by invoking Ringful idcheck method via HTTP GET
  4. Take pass code input from user and verify whether it matches the pass code sent to phone

Since the focus of this article is on using the Ringful RESTful APIs in your apps, we will skip steps 1,2 and 4 and only go into detail on step 3. The following code implements step 3. Its commented richly and is hopefully straightforward to understand. The main function is deliverCode(). The other function invokeRemote() offers a generic way to invoke Ringful API methods.


import time
import logging
import hashlib

from google.appengine.api import urlfetch


# The following values are just examples.
# You will obtain the actual values for your application from your Ringful Dev Zone account page.
APP_KEY = "746b1d1214b5c6c40198c73825dea3e34c2fcdf8"
APP_SIG_KEY = "**************************"

def deliverCode(callee):
'''
Generates a 5 digit code and send it to user's phone
'''
pass_code = str(random.randint(10000, 99999))
params = {
"callee": callee,
"code": pass_code
}
resp = invokeRemote("idcheck", params)
if resp.status_code == 200:
# the generated code is sent to the phone.We keep a copy in the session
self.session['verificationCode'] = code
# we expect the user to receive the code and enter it in the web widget.
# It should match the value generated on the server
else:
# for some reason the idcheck API invokation failed
setFlashMessage("Server error. Please try again later.")
# show a user friendly text message in the web widget
return resp

def invokeRemote(apiMethod, params={}, body=None):
'''
Invokes a remote Ringful API method and returns the method HTTP response
'''
params['seq'] = long(time.time()*1000) # sys time in micorseconds
param_str = urllib.urlencode(params)
# calculate signature parameter for the API invocation
sig = hashlib.sha1(param_str + APP_SIG_KEY)
sig_str
= hash.hexdigest()
# construct full HTTP GET URL
url = "http://devzone.ringful.com/%s/apikey/%s?%s&sig=%s" % (apiMethod, app_key, param_str, sig_str)
httpMethod = urlfetch.GET
headers = {}
logging.debug("Invoking Remote: %s" % url)
resp = urlfetch.fetch(url, body, httpMethod, headers)
if resp.status_code != 200:
if 'X-RingfulErrMessage' in resp.headers:
# Ringful provides info on API invocation problems in a response header

resp_err_msg = resp.headers['X-RingfulErrMessage']
else:
resp_err_msg = '(None)' # it is possible that the problem was outside Ringful. For example network connectivity issues.
err_msg = 'Error invoking %s API. [URL: %s] , Error [code: %d], [message: %s]' % (apiMethod, url, resp.status_code, resp_err_msg)
logging.warning(err_msg)
return resp


We hope the code above illustrates clearly how to write mobility mashups with the Ringful API. You should be now able to write your own mashups. We would love to hear about your work.

Feel free to post any questions or comments to this article. If you prefer private email communication, please send messages to support at ringful.com.

0 comments: