sunscrapers / djoser

REST implementation of Django authentication system.
MIT License
2.55k stars 458 forks source link

Overriding the Serializer Class of JWT create end point ( Providing a custom serializer ) #528

Open adshin21 opened 4 years ago

adshin21 commented 4 years ago

So, I'was using rest_framework_simplejwt for jwt auth in my project, with a custom TokenObtainPairSerializer to extend the support of creating token with email & password also.

Here is the serializer

class CustomTokenPairSerializer(TokenObtainPairSerializer):

    def validate(self, data):
        try:
            user = User.objects.filter(username=data.get("username")) or \
                User.objects.filter(email=data.get("username"))
        except User.DoesNotExist:
            raise serializers.ValidationError("No such user exists")

        credentials = {
            "username": "",
            "password": data.get("password")
        }

        if user:
            user = user.first()
            credentials["username"] = user.username

        return super().validate(credentials)

and the endpoint to this is

path(
        'token/',
        TokenObtainPairView.as_view(
            serializer_class=CustomTokenPairSerializer,
            permission_classes=(AllowAny, )
        ),
        name='token_obtain_pair'
    ),

then I include the djoser I found no way to override the Serializer, I included the same endpoint as djoser-jwt-create with my custom serializer but it isn't working. I know the url matching takes place from start to end, but I did not get the desired result.

path('api/v1/auth/jwt/create', TokenObtainPairView(
        serializer_class=CustomTokenPairSerializer,
        permission_classes=(AllowAny, )
    ).as_view(), name='jwtt-create'),
path('api/v1/auth/', include('djoser.urls')),
path('api/v1/auth/', include('djoser.urls.jwt')),

Thank You.

sud0su commented 3 years ago

I don't know if the class CustomTokenPairSerializer that you make is working, I'm not testing yet. but if you want to make a custom token. you can do this... this work on my project.

create new file serializers.py

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView

class TokenObtainSerializer(TokenObtainPairSerializer):
    def validate(self, attrs):
        data = super().validate(attrs)
        refresh = self.get_token(self.user)

        data["refresh"] = str(refresh)
        data["access"] = str(refresh.access_token)
        data["test"] = "value"

        return data

class CutomObtainPairView(TokenObtainPairView):
    serializer_class = TokenObtainSerializer

and then in your urls.py

from accounts.serializers import CutomObtainPairView

urlpatterns = [
    path('admin/', admin.site.urls),

    path('auth/jwt/create/', CutomObtainPairView.as_view(), name='customtoken'),
    path('auth/', include('djoser.urls')),
    path('auth/', include('djoser.urls.jwt')),
]

you will get the result like this :

{
    "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXB...........",
    "access": "eyJ0eXAiOiJKV1QiLCJh........bGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIj.",
    "test": "value"
}
danilochilene commented 1 year ago

If you want to return the data inside the token change the serializer as below:

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):

    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)

        token["email"] = user.email
        token["userRole"] = user.role

        return token