mart1n
mart1n

Reputation: 6233

Return values of custom authentication in Django REST Framework

I'm trying to write my basic custom authentication for Django REST Framework. I have the following auth backend:

class JoeAuth(authentication.BaseAuthentication):
    def authenticate(self, request):
        username = request.META.get('HTTP_X_FORWARDED_USER')
        if not username:
            return

        try:
            user = User.objects.get(krb_name=username, active=True).name
        except User.DoesNotExist:
            raise PermissionDenied('Unauthorized user')

        return (user, None)

Accompanied with a view:

@api_view()
def hello(request):
    return Response(data='hello')

And of course enabled in settings.py:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'helloworld.auth.JoeAuth',
    )
}

Now, if a request comes in and does not specify the HTTP_X_FORWARDED_USER header, the authenticate() function returns None. According so DRF docs:

To implement a custom authentication scheme, subclass BaseAuthentication and override the .authenticate(self, request) method. The method should return a two-tuple of (user, auth) if authentication succeeds, or None otherwise.

In some circumstances instead of returning None, you may want to raise an AuthenticationFailed exception from the .authenticate() method.

A None means authentication failed and should ideally return 401 or 403. However, in practice this doesn't seem to be the case. A request without the HTTP_X_FORWARDED_USER is simply allowed and 200 is returned:

$ http http://127.0.0.1:8000/ HTTP_X_FORWARDED_USER:joe
HTTP/1.1 200 OK

"hello"

$ http http://127.0.0.1:8000/
HTTP/1.1 200 OK

"hello"

Am I misunderstanding the documentation in the sense that a None is considered a successful authentication attempt?

Upvotes: 0

Views: 2202

Answers (1)

Ken4scholars
Ken4scholars

Reputation: 6296

The problem is that you are confusing authentication and authorization(permissions in Django). What the authentication does is identifyy the user, however it does not in any way, restrict the user - that is the work of authorization(permission). The permission classes do the work of checking the rights a user has over a particular resource. From what I can see, it seems you have a default global AllowAny permission set, which allows anyone access. You need to set the permission to restrict the endpoint to only authenticated users.

In your case, you need to add a permission_classes to the view or use global perm issions in DRF settings. You can add permission classes to a function-based API view this way:

from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import permission_classes, api_view


@api_view()
@permission_classes([IsAuthenticated])
def hello(request):
    return Response(data='hello')

Upvotes: 2

Related Questions