15

In Django, superuser can add more user according to their roll. I'm using simple JWT with DRF for authentication. But it is impossible to detect the type of user only by seeing the Access and Refresh Tokens.

Here are my settings.py file

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
    'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework_simplejwt.authentication.JWTAuthentication',),


}

urls.py

from django.contrib import admin
from django.urls import path, include
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView


urlpatterns = [

    path('admin/', admin.site.urls),
    path('', include('Manage_Merchants.urls')),

    path('api-auth', include('rest_framework.urls')),
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),


]

when I hit on 127.0.0.1:8000/api/token/ through Postman it asks for username and password. When I put Username and Password it generates a Refresh and Access Token. Generate JWT with DRF using Postman

So how can I identify the token is generated for super user or other user created bu superuser? How can I pass more value as a dictionary along with Access and Refresh Tokens to identify the type of user?

deepto
  • 181
  • 1
  • 1
  • 6

3 Answers3

25

In the version djangorestframework-simplejwt==4.4.0 it's the method validate instead of to_representation, meaning:

In your serializer.py you need to override the TokenObtainPairSerializer in order to include all the data you want to send in the response

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer


class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
    def validate(self, attrs):
        # The default result (access/refresh tokens)
        data = super(CustomTokenObtainPairSerializer, self).validate(attrs)
        # Custom data you want to include
        data.update({'user': self.user.username})
        data.update({'id': self.user.id})
        # and everything else you want to send in the response
        return data

Now in your views.py you need to override the TokenObtainPairView in order to pair it with the new serializer.

from .serializers import CustomTokenObtainPairSerializer


class CustomTokenObtainPairView(TokenObtainPairView):
    # Replace the serializer with your custom
    serializer_class = CustomTokenObtainPairSerializer

Now map your it in your url.py

from rest_framework_simplejwt.views import TokenRefreshView, TokenVerifyView
from . import views

urlpatterns = [
    # This one now has the custom view mapped with the custom serializer that includes the desired data
    path('token/', views.CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    path('token/verify/', TokenVerifyView.as_view(), name='token_verify')
]

Emmanuel Valdez
  • 419
  • 6
  • 9
  • 1
    I'm getting an error in the view saying that a Response must be returned. Have I missed something? – kingJulian Mar 11 '20 at 20:58
  • Could you share the stack trace, the view and the versions of the packages you're using? – Emmanuel Valdez Mar 14 '20 at 14:16
  • I haven’t tried this yet but this seems to be the info I need. Can I ask how did you find this out? I’ve looked through the docs. If they leave something to be desired. Did you have to just look at the source code? – Hanzy Apr 10 '20 at 12:31
  • I found this by digging through the github repo but usually it’s documented a bit more. Maybe I’ll make a PR and add to the docs unless it’s already documented? – Hanzy Apr 10 '20 at 12:37
  • 1
    @Hanzy I'm not 100% sure, but I know the [Official Docs](https://www.django-rest-framework.org/api-guide/authentication/#json-web-token-authentication), a blog on [SimpleIsBetternThanComplex](https://simpleisbetterthancomplex.com/tutorial/2018/11/22/how-to-implement-token-authentication-using-django-rest-framework.html), a lot of googling and an Ionic tutorial I'm doing that some how gave some ideas. – Emmanuel Valdez Apr 12 '20 at 03:05
10

Like kumar said, you should override TokenObtainPairView. Let me get deeper in it:

Create a new classView in your core app views.py, or if you want to have a cleaner code, you could create a new app for example called jwt_token_patched and create a views.py file in it. Now add below code to it:

class TokenObtainPairPatchedView(TokenObtainPairView):
    """
    Takes a set of user credentials and returns an access and refresh JSON web
    token pair to prove the authentication of those credentials.
    """
    serializer_class = serializers.TokenObtainPairPatchedSerializer

    token_obtain_pair = TokenObtainPairView.as_view()

Now for serializer add this:

class TokenObtainPairPatchedSerializer(TokenObtainPairSerializer):
     def to_representation(self, instance):
         r = super(TokenObtainPairPatchedSerializer, self).to_representation(instance)
         r.update({'user': self.user.username})
         return r

method to_representation() is called when serializer is returning data in json format, so you could add anything that you want in there. remember I just put username in user field value, you can add any item value of user that you want in it.

Also create a url for this and from now on use that method for getting token. Feel free to ask any questions if you want. hope it was usefull :)

Reza Torkaman Ahmadi
  • 2,958
  • 2
  • 20
  • 43
  • brother I am a absolute beginner. I can not follow your instruction properly. I would be very glad if you edit my code so that I can see the username along with the access token. clone my code from here: https://github.com/SabitDeepto/DRF.git – deepto Nov 29 '18 at 07:09
  • A good answer should also include needed imports to run the code. – Silidrone Jul 11 '21 at 11:01
2

For customizing refresh token the best thing you can do is override the "TokenRefreshSerializer" which is shown below. However if you want to get any field form the model we have to decode the token to get the UUID of the user. This can be done using token_backend

Note: do make sure you're using "rest_framework_simplejwt" not "djangorestframework-jwt" since it's been deprecated

from rest_framework_simplejwt.serializers import TokenRefreshSerializer
from rest_framework_simplejwt.state import token_backend

class CustomTokenRefreshSerializer(TokenRefreshSerializer):
    def validate(self, attrs):
        data = super(CustomTokenRefreshSerializer, self).validate(attrs)
        decoded_payload = token_backend.decode(data['access'], verify=True)
        user_uid=decoded_payload['user_id']
        # add filter query
        data.update({'custom_field': 'custom_data')})
        return data

And then use this serializer as shown below with "CustomTokenRefreshView" which inherits "TokenRefreshView"

from rest_framework_simplejwt.views import TokenRefreshView
class CustomTokenRefreshView(TokenRefreshView):
    """
    Custom Refresh token View
    """
    serializer_class = CustomTokenRefreshSerializer

And add this in urls

path('api/token/refresh/', CustomTokenRefreshView.as_view(), name='token_refresh'),
Raj Chhatbar
  • 23
  • 1
  • 4