Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement djangorestframework-simplejwt token authentication without password

My app does not require password as I want to login with phone and OTP. I'm trying to implement custom simple JWT token authentication which takes only a phone number and no passwords. I'm new to Django and I did check some links in stackoverflow and tried this:

class CustomSerializer(TokenObtainPairSerializer):

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.fields[self.username_field] = serializers.CharField()
    del self.fields['password']



def validate(self,attr):
    print(attr)
    
    data = super().validate(attr)
    token = self.get_token(self.user)
    print (token)
    try:
        request = self.context["request"]
        print(request)
    except KeyError:
        pass
    request_data = json.loads(request.body)
    print(request_data)
 

So here when validate method is executed, it goes to validate TokenObtainPairSerializer init method which in return calls init method of it's parent class which is validating the password. So even if I'm deleting password field in my custom serializer, it still gives me a key-error of password. I tried to pass the key-error but again it gets failed at request.body.

I'm stuck on this and I don't know how to implement simple JWT without password.

like image 831
user190549 Avatar asked Jul 05 '20 09:07

user190549


1 Answers

I had the same question and after a lot of searching and reading the source code of django-rest-framework-simplejwt I got an answer.

So even if i am deleting passowrd field in my custom serializer, it still give me key-error of password

If you take a look at the TokenObtainSerializer class, which is the parent Serializer of TokenObtainPairSerializer here, you can see that the password is called like this:

# rest_framework_simplejwt/serializers.py

def validate(self, attrs):
    authenticate_kwargs = {
        self.username_field: attrs[self.username_field],
        'password': attrs['password'],
    }
    # ...

So even though you delete the password field, it is still called later on.

What I did was setting the password field as not required and assigning an empty string as password.

class TokenObtainPairWithoutPasswordSerializer(TokenObtainPairSerializer):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['password'].required = False

    def validate(self, attrs):
        attrs.update({'password': ''})
        return super(TokenObtainPairWithoutPasswordSerializer, self).validate(attrs)

Now it is possible to use this Serializer in a View.

class TokenObtainPairWithoutPasswordView(TokenViewBase):
    serializer_class = TokenObtainPairWithoutPasswordSerializer

Then I created a custom authentication backend so that the user can authenticate without a password.

from django.contrib.auth.backends import BaseBackend


class AuthenticationWithoutPassword(BaseBackend):

    def authenticate(self, request, username=None):
        if username is None:
            username = request.data.get('username', '')
        try:
            return User.objects.get(username=username)
        except User.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

You can read the docs for more information on creating your custom authentication backend.

Finally, on settings.py change your AUTHENTICATION_BACKENDS variable.

AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend',
    'core.custom_authentication.AuthenticationWithoutPassword',
)

Now Django will try to authenticate using the first authentication ModelBackend and then the new AuthenticationWithoutPassword.

Just saying the obvious here, but keep in mind that authentication without password is definitely not safe, so you should add more logic to your custom authentication, remember that you can access the request variable.

like image 181
Derik Oliveira Avatar answered Dec 01 '22 01:12

Derik Oliveira