Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the right way to write a django-piston client?

I've been reading a lot on django-piston and using to to make an API for an app I'm development, but I'm getting hung up on the client side of the world. I've written the handlers and uri mappings, and I can return JSON or XML to my heart's content. Where I'm getting stuck is what to do with that now.

My ideal endgame is to have an iPhone and Android client consume and return data, but I don't know the right way to handle authentication. The easiest way I can figure is saving the username and password on the device and tagging each request with it, ultimately using Basic Authentication, but that wreaks of wrong. I've looked in to piston's support for OAuth and gotten it working with the help of this tutorial, but that doesn't feel like the right answer, either. Ultimately, I'd really like to have a simple prompt on the device for username and password, those will be sent up to Django via Piston and REST, and an API key will return down. The device will store that key and tag all subsequent requests with it. That feels like the right way, but I can't figure out how to do it. Can any one point me in the right direction?

like image 450
eddieroger Avatar asked Dec 16 '10 05:12

eddieroger


1 Answers

You can write your own authentication module. Here's an example:

class ApiKeyAuthentication(object):

    def is_authenticated(self, request):
        auth_string = request.META.get("HTTP_AUTHORIZATION")

        if not auth_string:
            return False

        key = get_object_or_None(ApiKey, key=auth_string)

        if not key:
            request.user = AnonymousUser()
            return False

        request.user = key.user

        return True

    def challenge(self):
        resp = HttpResponse("Authorization Required")
        resp['WWW-Authenticate'] = "Key Based Authentication"
        resp.status_code = 401
        return resp

You'll need a model to store a mapping of API keys to Users:

class ApiKey(models.Model):
    user = models.ForeignKey(User, related_name='keys')
    key = models.CharField(max_length=KEY_SIZE)

You'll need some method to generate the actual keys. Something like this will work (say, in the ApiKey model's save method:

key = User.objects.make_random_password(length=KEY_SIZE)

while ApiKey.objects.filter(key__exact=key).count():
    key = User.objects.make_random_password(length=KEY_SIZE)

Lastly, hook up your new authentication backend:

# urls.py

key_auth = ApiKeyAuthentication()

def ProtectedResource(handler):
    return resource.Resource(handler=handler, authentication=key_auth)

your_handler = ProtectedResource(YourHandler)

As for swapping username / password for an API key, just write a handler that uses BasicAuthentication to create and return new ApiKey (for request.user).

like image 192
Chris Lawlor Avatar answered Oct 24 '22 05:10

Chris Lawlor