Authentication with three fields in django-rest-framework-simple-jwt

Issue

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 {}

Solution

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
            )

How to create tokens manually from Simple JWT docs

Answered By – Mohamed Hamza

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published