Dan 'Staer' Harris

...ramblings of a code monkey...

Multiple Authentication with Django-Piston

One of the problems that I hit with django-piston recently was that I needed to support more than a single type of authentication for my REST API. In one case I needed an authentication handler that authenticated against the the Django auth system itself so that I could use the API to dynamically populate choice fields on the fly using the currently logged in website user. An implementation of a DjangoAuthentication piston module can be found here. The other type of auth handler I needed was the HttpBasicAuthentication (built into piston) handler so that 3rd party tools could access the exact same API that the website was using.

Django-Piston does not by default allow more than one authentication system to work together out of the box, so the following is a quick and dirty implementation of piston authentication handler that authenticates using a list of different auth modules:

	class MultiAuthentication(object):
	    """ Authenticated Django-Piston against multiple types of authentication """

	    def __init__(self, auth_types):
	        """ Takes a list of authenication objects to try against, the default
	        authentication type to try is the first in the list. """
	        self.auth_types = auth_types
	        self.selected_auth = auth_types[0]

	    def is_authenticated(self, request):
	        """ Try each authentication type in order and use the first that succeeds """
	        authenticated = False
	        for auth in self.auth_types:
	            authenticated = auth.is_authenticated(request)
	            if authenticated:
	                selected_auth = auth
	                break
	        return authenticated

	    def challenge(self):
	        """ Return the challenge for whatever the selected auth type is (or the default 
	        auth type which is the first in the list)"""
	        return self.selected_auth.challenge()
	

The way this works is that you pass in a list of different authentication types to the MultiAuthentication constructor which you want to test against. The first item in the list is considered the ‘default’. When authentication each auth module in the list is tried in order until one succeeds or they all fail. If each method fails then the ‘default’ challenge will be returned to the user. Example usage:

	from piston.authentication import HttpBasicAuthentication
	from somewhere.else import DjangoAuthentication
	
	# Authenticate against HttpBasic and then Django if basic fails. If both 
	# methods fail, ask the user for username/password via HttpBasic
	basicAuth = HttpBasicAuthentication(realm="Test")
	djangoAuth = DjangoAuthentication()
	auth = MultiAuthentication([basicAuth, djangoAuth])
	

This is available in gist form at https://gist.github.com/790222

comments powered by Disqus
Fork me on GitHub