Search code examples
djangoauthenticationdjango-rest-frameworkcustom-authentication

Django Rest Framework Custom authentication + Basic Auth


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?


Solution

  • @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)