Django Rest JWT login using username or email? Django Rest JWT login using username or email? django django

Django Rest JWT login using username or email?


No need to write a custom authentication backend or custom login method.

A Custom Serializer inheriting JSONWebTokenSerializer, renaming the 'username_field' and overriding def validate() method.

This works perfectly for 'username_or_email' and 'password' fields where the user can enter its username or email and get the JSONWebToken for correct credentials.

from rest_framework_jwt.serializers import JSONWebTokenSerializerfrom django.contrib.auth import authenticate, get_user_modelfrom django.utils.translation import ugettext as _from rest_framework import serializersfrom rest_framework_jwt.settings import api_settingsUser = get_user_model()jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLERjwt_encode_handler = api_settings.JWT_ENCODE_HANDLERjwt_decode_handler = api_settings.JWT_DECODE_HANDLERjwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLERclass CustomJWTSerializer(JSONWebTokenSerializer):    username_field = 'username_or_email'    def validate(self, attrs):        password = attrs.get("password")        user_obj = User.objects.filter(email=attrs.get("username_or_email")).first() or User.objects.filter(username=attrs.get("username_or_email")).first()        if user_obj is not None:            credentials = {                'username':user_obj.username,                'password': password            }            if all(credentials.values()):                user = authenticate(**credentials)                if user:                    if not user.is_active:                        msg = _('User account is disabled.')                        raise serializers.ValidationError(msg)                    payload = jwt_payload_handler(user)                    return {                        'token': jwt_encode_handler(payload),                        'user': user                    }                else:                    msg = _('Unable to log in with provided credentials.')                    raise serializers.ValidationError(msg)            else:                msg = _('Must include "{username_field}" and "password".')                msg = msg.format(username_field=self.username_field)                raise serializers.ValidationError(msg)        else:            msg = _('Account with this email/username does not exists')            raise serializers.ValidationError(msg)

In urls.py:

url(r'{Your url name}$', ObtainJSONWebToken.as_view(serializer_class=CustomJWTSerializer)),


Building on top of Shikhar's answer and for anyone coming here looking for a solution for rest_framework_simplejwt (since django-rest-framework-jwt seems to be dead, it's last commit was 2 years ago) like me, here's a general solution that tries to alter as little as possible the original validation from TokenObtainPairSerializer:

from rest_framework_simplejwt.serializers import TokenObtainPairSerializerclass CustomJWTSerializer(TokenObtainPairSerializer):    def validate(self, attrs):        credentials = {            'username': '',            'password': attrs.get("password")        }        # This is answering the original question, but do whatever you need here.         # For example in my case I had to check a different model that stores more user info        # But in the end, you should obtain the username to continue.        user_obj = User.objects.filter(email=attrs.get("username")).first() or User.objects.filter(username=attrs.get("username")).first()        if user_obj:            credentials['username'] = user_obj.username        return super().validate(credentials)

And in urls.py:

url(r'^token/$', TokenObtainPairView.as_view(serializer_class=CustomJWTSerializer)),


Found out a workaround.

@permission_classes((permissions.AllowAny,))def signin_jwt_wrapped(request, *args, **kwargs):    request_data = request.data    host = request.get_host()    username_or_email = request_data['username']    if isEmail(username_or_email):        # get the username for this email by model lookup        username = Profile.get_username_from_email(username_or_email)        if username is None:            response_text = {"non_field_errors":["Unable to login with provided credentials."]}            return JSONResponse(response_text, status=status.HTTP_400_BAD_REQUEST)    else:        username = username_or_email    data = {'username': username, 'password':request_data['password']}    headers = {'content-type': 'application/json'}    url = 'http://' + host + '/user/signin_jwt/'    response = requests.post(url,data=dumps(data), headers=headers)    return JSONResponse(loads(response.text), status=response.status_code)

I check that whether the text that I received is a username or an email.

If email then I lookup the username for that and then just pass that to /signin_jwt/