I am working on custom authentication in DRF. User should send 3 fields (phone, email, password).
I was following this answer Adding custom user authentication to django-rest-framework-simple-jwt
(I was overriding TokenObtainPairSerializer
and TokenObtainPairView
)
I was using this source code https://github.com/jazzband/djangorestframework-simplejwt/blob/master/rest_framework_simplejwt/serializers.py
Problem: for the request below I received an empty response {}
but expected tokens or error message
{
"email": "[email protected]",
"password": "admin123456",
"phone_number": "123132"
}
My code: views.py
class CustomTokenObtainPairView(TokenObtainPairView):
serializer_class = CustomTokenObtainPairSerializer
serializers.py
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
username_field = CustomUser.USERNAME_FIELD
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields[self.username_field] = serializers.CharField()
self.fields['password'] = PasswordField()
self.fields['phone_number'] = serializers.CharField() # authentication requires 3 fields
def validate(self, attrs):
authenticate_kwargs = {
self.username_field: attrs[self.username_field],
'password': attrs['password'],
'phone_number': attrs['phone_number']
}
try:
authenticate_kwargs["request"] = self.context["request"]
except KeyError:
pass
self.user = authenticate(**authenticate_kwargs)
if not api_settings.USER_AUTHENTICATION_RULE(self.user):
raise exceptions.AuthenticationFailed(
self.error_messages["no_active_account"],
"no_active_account",
)
return {}
CodePudding user response:
Let's try to login and generate a token manually:
serializers.py
from rest_framework import serializer
class LoginUserSerializer(serializers.Serializer):
email = serializers.EmailField()
phone_number = serializers.CharField()
password = serializers.CharField(write_only=True)
views.py
from django.contrib.auth import authenticate
from rest_framework import status, serializers
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.tokens import RefreshToken
class LoginUserApi(APIView):
def post(self, request):
serializer = LoginUserSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data # Fetch the data form serializer
user = authenticate(email=data['email'], password=data['password']) # check for email and password
if not user or user.phone_number != data['phone_number']: # check for phone
raise serializers.ValidationError({'detail':'Incorrect email, phone, or password'})
# Generate Token
refresh = RefreshToken.for_user(user)
return Response(
{
'access': str(refresh.access_token),
'refresh': str(refresh)
}
, status=status.HTTP_200_OK
)
CodePudding user response:
Use this code
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
data = super().validate(attrs)
data['username'] = self.user.email
data['email'] = self.user.email
serializer = serializers.UserSerializerWithToken(self.user).data
for k, v in serializer.items():
data[k] = v
return data
## login view
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
Serializer files
class UserSerializer(serializers.ModelSerializer):
isStaffAdmin = serializers.SerializerMethodField(read_only=True)
class Meta:
model = CustomerTable
fields = ['id', 'username', 'email', 'isStaffAdmin']
def get_isStaffAdmin(self, obj):
return obj.is_staff
class UserSerializerWithToken(UserSerializer):
token = serializers.SerializerMethodField(read_only=True)
class Meta:
model = CustomerTable
fields = ['id', 'username', 'email', 'isStaffAdmin', 'token']
def get_token(self, obj):
token = RefreshToken.for_user(obj)
return str(token.access_token)
def get_isStaffAdmin(self, obj):
if obj.is_staff and obj.is_active:
return 2
else:
error = {'message': "Invalid User"}
raise serializers.ValidationError(error)
U can customize this code according to your needs.