2

I am using the Django Rest Framework in my Python-Django app, and am using a Custom authentication for the api.

If I work just with my custom authentication method, works correctly.

 @authentication_classes((CustomAuthentication,  ))

But If I try to have basic authentication and my custom authentication, in that order, my custom authentication never executes. I mean, I want that if the Basic authentication fails, then try with the custom authentication. The Basic Authentication is executes and then ends.

@authentication_classes((SessionAuthentication, BasicAuthentication, CustomAuthentication ))

Is possible to have at the same time this three authentication methods, and execute them in that order?

david cerezal
  • 23
  • 1
  • 4

2 Answers2

0

The Django Rest Framework authentication documentation clearly states this:

The authentication schemes are always defined as a list of classes. REST framework will attempt to authenticate with each class in the list, and will set request.user and request.auth using the return value of the first class that successfully authenticates.

If no class authenticates, request.user will be set to an instance of django.contrib.auth.models.AnonymousUser, and request.auth will be set to None.

So whenever your first class authenticates request.user and request.auth is set.

In case you want to authenticate with your CustomAuthentication class return None for BasicAuthentication so that all your authentication classes are used but the user is set as per your CustomAuthentication

Arpit Goyal
  • 2,212
  • 11
  • 31
  • if the first authentication class failed, it will raise an Exception, so the second authentication class will never be reached. – soooooot Aug 19 '15 at 09:13
  • considering the input of @soooooot here is the documentation which states django rest framework's authentication in case of unauthorized or forbidden responses. – Arpit Goyal Aug 19 '15 at 09:19
0

@Arpit Goyal's answer makes the workflow clear.

If you INDEED want to go through all the authentication classes,

here's a work around solution you can try. I hope it can help you.

@authentication_classes((AuthencationWrapper,  ))

add a AuthencationWrapper

class AuthencationWrapper(BaseAuthentication):

    authenication_classes = (
        BasicAuthentication,
        SessionAuthentication,
        CustomAuthentication,
    )

    def authenticate(self, request):
        exc = None
        ret = None
        for auth in self.authentication_classes:
            try:
                ret = auth().authenticate(request)
                # if success, we will break, else we will continue to call next one
                break
            except exceptions.AuthenticationFailed as e:
                # we only record the first failed exception
                if exc is None:
                    exc = e
                    self.first_failed_auth = auth()
        if ret is None:
            raise exc

        # one of the authentication_classes is passed
        return ret

    def authenticate_header(self, request):
        # actualy this way breaks what django-rest-framework doing now
        return self.first_failed_auth.authenticate_header(request)

        # the one follows what django-rest-framework doing now
        # choose the first authentication class header
        ## if self.authentication_classes:
        ##      return self.authentication_classes[0].authenticate_header(request)
soooooot
  • 1,699
  • 13
  • 18
  • Having that error `AttributeError: 'WSGIRequest' object has no attribute 'successful_authenticator `. Find that issue, but seems to not be the same: [stacklink](http://stackoverflow.com/questions/16736710/wsgirequest-object-has-no-attribute-successful-authenticator) – david cerezal Aug 20 '15 at 08:08
  • if your `CustomAuthentication` fails, do you raise an `exceptions.AuthenticationFailed` error? – soooooot Aug 20 '15 at 08:33
  • try just with `authenication_classes = ( BasicAuthentication, SessionAuthentication )` And having the same error. – david cerezal Aug 20 '15 at 08:43
  • Solved, seems that `ret = auth().authenticate(request)`was not instantiating correctly the class, have to do in another line. – david cerezal Aug 20 '15 at 11:24