4

I am using Django Token-based Authentication. (JWT Token is generated by a third-party service like AWS Cognito, we will just verify signature and expiry time).

This REST Application will not have any user models, whoever consuming the API calls needs to be authenticated by JWT token only.

class JSONWebTokenAuthentication(TokenAuthentication):
    def authenticate_credentials(self, jwtToken):
        try:
            payload = jwt.decode(jwtToken, secret_key,verify=True)
            # user = User.objects.get(username='root')
            user =  AnonymousUser()
        except (jwt.DecodeError, User.DoesNotExist):
            raise exceptions.AuthenticationFailed('Invalid token')
        except jwt.ExpiredSignatureError:
            raise exceptions.AuthenticationFailed('Token has expired')
        return (user, payload)

In Views:

@api_view(["POST"])
@authentication_classes((JSONWebTokenAuthentication,))
@permission_classes((AllowAny,))

The above process doesn't keep track of Token at all. With/Without Token, API calls are working. If I make two changes as below, it is working.

user = User.objects.get(username='root')
#user = AnonymousUser()
@permission_classes((IsAuthenticated,))

One way to do it is, to have at least one user in my app and reference that user[ This web app might scale to any number of instances when needed, so inserting the same user with the same "username" has to be automated. ]. But instead, can I eliminate "User" concept in Authentication?

Matt Wang
  • 318
  • 1
  • 10
AI Python
  • 43
  • 1
  • 3

4 Answers4

9

Sometimes you just really don't need a User, for example, server to server communication. Here is a solution.

Override the AnonymousUser's is_authenticated property and you are good to go

from django.contrib.auth.models import AnonymousUser

class ServerUser(AnonymousUser):

    @property
    def is_authenticated(self):
        # Always return True. This is a way to tell if
        # the user has been authenticated in permissions
        return True

Simply return this new type of user in your custom Authentication

class CustomServerAuthentication(authentication.BaseAuthentication):
    keyword = 'Token'

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

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

        if len(auth) == 1:
            raise exceptions.AuthenticationFailed('Invalid token header. No credentials provided.')

        elif len(auth) > 2:
            raise exceptions.AuthenticationFailed('Invalid token header. Token string should not contain spaces.')

        token = auth[1].decode()

        if not (settings.CUSTOM_SERVER_AUTH_TOKEN == token):
            raise exceptions.AuthenticationFailed('You do not have permission to access this resource')

        user = ServerUser()

        return user, None
Dr Manhattan
  • 13,537
  • 6
  • 45
  • 41
  • If you don't want to rewrite the token keyword parsing, you can inherit from `TokenAuthentication` and just override the `authenticate_credentials` method – Tudax Mar 01 '22 at 15:53
6

Using django-rest-framework-simplejwt you can set DEFAULT_AUTHENTICATION_CLASSES to use JWTTokenUserAuthentication and just validate the token even without a user.

Rodrigo Braga
  • 139
  • 2
  • 6
5

Django REST framework largely assumes that requests are authenticated based on a user, but they do provide support for authentication anonymous requests. But it stands out from the standard assumption of "verifying (django) user is genuine" by giving anonymous user with certain permissions. The problem with your first case is permission decorator with "Allow Any".

I suggest to have a dummy Django user. (it doesn't stop you from scaling to any number of instances as well).

Use

user = User.objects.get_or_create(username='whatever')[0]

instead of

user =  AnonymousUser()

Now change the permission decorator to

@permission_classes((IsAuthenticated,))

This user cannot be logged in by anyone unless you set a password, moreover logging in as this user will not give you access to your API call. The only way to access your API is by sending a valid Token.

Hope this helps.

Satish V Madala
  • 182
  • 2
  • 12
  • 2
    Thanks. It worked as an workaround for me. When am using thirdparty tools like AWS cognito, I believe we shouldn't care about Django User model at all. – AI Python Jun 01 '17 at 10:14
0

you can use User model without inserting data to database use:

user = User(id=22,username="someone")

instead of:

user = User.objects.get_or_create(username="someone")

or

 AnonymousUser()