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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With