Home > Software design >  Authentication with three fields in django-rest-framework-simple-jwt
Authentication with three fields in django-rest-framework-simple-jwt

Time:05-08

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.

  • Related