How to pass multiple authentication_classes in django rest framework?

Viewed 4186

I want to create an api which can be accessed using client_id and client_secret as well as access token. To access api using client_id and client_secret I have a custom authentication class like this:-

class ClientAuthentication(authentication.BaseAuthentication):
    @staticmethod
    def get_client_credentials(request):
        try:
            client_id = request.headers.get('CLIENTID')
            client_secret = request.headers.get('CLIENTSECRET')
        except:
            raise AuthenticationFailed
        return {
            'client_id': client_id,
            'client_secret': client_secret
        }

    def authenticate(self, request):
        credentials = self.get_client_credentials(request)

        client_instance = Application.objects.filter(
            client_id=credentials['client_id'],
            client_secret=credentials['client_secret'],
        ).first()

        if not client_instance:
            raise AuthenticationFailed
        return client_instance, None

    def get_user_application(self, request):
        credentials = self.get_client_credentials(request)

        application_instance = Application.objects.filter(
            client_id=credentials['client_id'],
            client_secret=credentials['client_secret'],
        ).first()

        if not application_instance:
            raise AuthenticationFailed
        return application_instance, None

And the other authentication class i want to use is default OAuth2Authentication I tried passing the authentication_classes for viewset as:

authentication_classes = [ClientAuthentication | OAuth2Authentication]

But it gives me an error. How can I achieve this?

1 Answers

You should differentiate the authenticators by the authenticate_header. When an AuthenticationClass (in the authenticate method) read an incorrect authenticate_header, should return None, so the request continue to the next Authenticator. For example, in TokenAuthentication

def authenticate(self, request):
    auth = get_authorization_header(request).split()

    if not auth or auth[0].lower() != self.keyword.lower().encode():
        return None
    ...

If you don't return None, the authenticate will raise an APIException, so the next authentication class will not be called.

Also, if you want to use multiple Authentication Classes, you must define them in settings as show in the documentation:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ]
}

Or define them in the view.

Related