Application and user authentication

143 views
Skip to first unread message

Max Arnold

unread,
Sep 23, 2012, 12:55:15 AM9/23/12
to django-res...@googlegroups.com
Hi all!

What is the best approach to implement two authentication tokens (both in current version and upcoming d-r-f 2.0)? I want to
have per-application (developer) token to secure all API endpoints and second one for normal user authentication.

Also I'd like to have different metadata attached to these tokens (to use it in permission classes). For example I want to limit
per-app API feature set to disable some endpoints or to enable additional debugging for developer tokens. And per-user authorization
will control object-level access.

As far as I know, current authentication logic allows to return only one user object.

Tom Christie

unread,
Sep 26, 2012, 7:56:58 AM9/26/12
to django-res...@googlegroups.com
I take it you mean two authentication methods that always need to *both* run, rather than two authentication methods, either of which might succeed?

Right now that'd be a little awkward.  You could override the logic in AuthMixin, but it'd probably be easiest to just shoehorn the logic into a single authentication class and set additional properties on the user instance before returning it if you need to add extra metadata.

Cue the familiar line "this'll be better in 2.0", both because:

* It'a more simple to override the authentication behavior to allow multiple authentication policies to succeed.
* Authentication classes return both a `user` and an `auth` object, which makes it easier to return custom authentication data.

Help any?

  Tom

David Kuhn

unread,
Sep 26, 2012, 5:39:56 PM9/26/12
to django-res...@googlegroups.com
Hey Max,

I implemented an API key authentication scheme by copying the existing BasicAuthentication class and changing the Authorization header check to look for the presence of "apikey" instead of "basic". For example:
 
import base64
import re
from djangorestframework.authentication import BaseAuthentication
from hubify.profile.models import UserProfile

class ApiKeyAuthentication(BaseAuthentication):
    """
    Use HTTP API Key authentication.
    """

    def authenticate(self, request):
        """
        Returns a :obj:`User` if a correct username and api_key have been supplied
        using HTTP API Key authentication.  Otherwise returns :const:`None`.
        """
        from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError

        if 'HTTP_AUTHORIZATION' in request.META:
            auth = request.META['HTTP_AUTHORIZATION'].split()
            if len(auth) == 2 and auth[0].lower() == 'apikey':
                try:
                    auth_parts = base64.b64decode(auth[1]).partition(':')
                except TypeError:
                    return None

                try:
                    username, api_key = smart_unicode(auth_parts[0]), smart_unicode(auth_parts[2])
                except DjangoUnicodeDecodeError:
                    return None

                try:
                    profile = UserProfile.objects.get(api_key=api_key)
                except UserProfile.DoesNotExist:
                    return None

                user = profile.user
                if user.username == username and user.is_active:
                    return user

        return None

With that in place all I did was create a new BaseModelView that overrode the authentication property:

class BaseModelView(ModelMixin, ModelView):
    permissions = (IsAuthenticated, )
    authentication = (UserLoggedInAuthentication,
                      BasicAuthentication,
                      ApiKeyAuthentication)

Can't comment on how this would mesh with 2.0 because I haven't used it.

Cheers,
Dave

Max Arnold

unread,
Sep 26, 2012, 8:51:50 PM9/26/12
to django-res...@googlegroups.com
On Wed, Sep 26, 2012 at 04:56:58AM -0700, Tom Christie wrote:
> I take it you mean two authentication methods that always need to *both*
> run, rather than two authentication methods, either of which might succeed?

Yes, exactly. And they authenticate different entities (applications and users of these applications).

> Right now that'd be a little awkward. You could override the logic in
> AuthMixin, but it'd probably be easiest to just shoehorn the logic into a
> single authentication class and set additional properties on the user
> instance before returning it if you need to add extra metadata.
>
> Cue the familiar line "this'll be better in 2.0", both because:
>
> * It'a more simple to override the authentication behavior to allow
> multiple authentication policies to succeed.
> * Authentication classes return both a `user` and an `auth` object, which
> makes it easier to return custom authentication data.
>
> Help any?

Thanks, I'll try this in code.

Also I think it can be easier to just check application token in middleware.
Reply all
Reply to author
Forward
0 new messages