4

I want to add user login via One-time Password as well as the usual username/password method in django. In order to do so, either username/password or username/OTP are sent from client to sever and based on the provided pair of fields, I need to return access and refresh token if the user is authenticated. I am using django's simple-jwt. I know that I have to override TokenObtainPairView and TokenObtainSerializer. The problem is, I want to do the field validation part myself.

In my views, I override simple-jwt's default view.

#views.py

class MyTokenObtainPairView(TokenObtainPairView):
    serializer_class = MyTokenObtainPairSerializer

And I override the serializer like below:

#serializers.py

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):

    def validate(self, attrs):
        try:
            request = self.context["request"]
        except KeyError:
            pass

        try:
            request_data = json.loads(request.body)
            if("username" in request_data and "password" in request_data):
                # default scenario in simple-jwt  
                pass
            elif("username" in request_data and "otp" in request_data):                                   
                # validate username/otp manually and return access/token pair if successful
                pass

            else:
                # some fields were missing
                raise serializers.ValidationError({"username/otp or username/password" : "These fields are required"})

        except:
            pass

So, if client passes user credentials in one of the possible forms below, I will be able to authenticate it and return token pair.

{
   "username" : "Winston",
   "password" : "testpass"
}

or

{
    "username" : "Winston",
    "otp" : "testotp"
}

The problem is, when I send data in the second form, I get 400 BadRequest:password is required. How can I customize fields and their validation?

1
  • 1
    What is the reason to stick into single serializer? Alternative way to write 2 separate serializer for password and OTP. Commented Apr 18, 2020 at 4:28

2 Answers 2

3

As Saiful Azad mentioned in comments, one possible method is to use separate serializers for each scenario.

#views.py

class MyTokenObtainPairView(TokenObtainPairView):
    def get_serializer_class(self):
        if ("otp" in self.request.data):
            return MyTokenObtainPairSerializer
        return TokenObtainPairSerializer

Then, you can implement your own serializer for otp verification. I used simple-jwt's implementation to implement my own serializer and use my custom authentication method.

Sign up to request clarification or add additional context in comments.

Comments

0

In your urls.py

# Imports
from rest_framework_simplejwt.tokens import RefreshToken
from django.contrib.auth.models import User
from rest_framework.response import Response
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny

@api_view(['GET'])
@permission_classes([AllowAny])
def get_tokens_for_user(request):

    # find the user base in params
    user = User.objects.first()

    refresh = RefreshToken.for_user(user)

    return Response({ 
       'refresh': str(refresh),
       'access': str(refresh.access_token),
    })

urlpatterns = [
    path('login', get_tokens_for_user, name="login")
]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.