Djangorestframework-simplejwt: User Creation and Custom Responses

Created on 17 Jul 2018  路  4Comments  路  Source: jazzband/djangorestframework-simplejwt

David, thanks for the great package.

I had a couple of issues I was running into that hopefully have an easy fix.

First was authenticating Users upon account creation. I tried implementing a serializer that inherited from TokenObtainPairSerializer.

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super(MyTokenObtainPairSerializer, cls).get_token(user)
        access = token.access_token
        return token, access

Upon authentication, I would add the token to the payload I passed to the frontend. The problem with this function is that it returned a jti, expiration and token type, but the actual token always returned None.

The other issues I wanted to tackled was modifying the MyTokenObtainPair end point to include user information in the Response.

Thanks for the guidance.

Most helpful comment

I have this working reasonably well, but the code could probably be a bit cleaner.

If I post a username and password to this view, a User is created and the response contains my access tokens with a claim added for the user's email address.

from rest_framework import serializers, permissions, status
from rest_framework.generics import CreateAPIView
from rest_framework.response import Response
from rest_framework.validators import UniqueValidator
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from bp_auth.models import CustomUser as User


class SignupSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)
    email = serializers.CharField(
        validators=[UniqueValidator(queryset=User.objects.all())]
    )

    class Meta:
        model = User
        fields = ('email',  'password')


class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super(MyTokenObtainPairSerializer, cls).get_token(user)
        token['email'] = user.email
        return token


class RegisterUsers(CreateAPIView):
    serializer_class = SignupSerializer
    permission_classes = (permissions.AllowAny,)

    def post(self, request, *args, **kwargs):
        serialized_user = SignupSerializer(data=request.data)
        if serialized_user.is_valid():
            User.objects.create_user(
                serialized_user.initial_data['email'],
                serialized_user.initial_data['password']
            )

            tokens = MyTokenObtainPairSerializer(request.data).validate(request.data)
            return Response(tokens, status=status.HTTP_201_CREATED)
        else:
            return Response(serialized_user._errors, status=status.HTTP_400_BAD_REQUEST)

All 4 comments

I have this working reasonably well, but the code could probably be a bit cleaner.

If I post a username and password to this view, a User is created and the response contains my access tokens with a claim added for the user's email address.

from rest_framework import serializers, permissions, status
from rest_framework.generics import CreateAPIView
from rest_framework.response import Response
from rest_framework.validators import UniqueValidator
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from bp_auth.models import CustomUser as User


class SignupSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)
    email = serializers.CharField(
        validators=[UniqueValidator(queryset=User.objects.all())]
    )

    class Meta:
        model = User
        fields = ('email',  'password')


class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super(MyTokenObtainPairSerializer, cls).get_token(user)
        token['email'] = user.email
        return token


class RegisterUsers(CreateAPIView):
    serializer_class = SignupSerializer
    permission_classes = (permissions.AllowAny,)

    def post(self, request, *args, **kwargs):
        serialized_user = SignupSerializer(data=request.data)
        if serialized_user.is_valid():
            User.objects.create_user(
                serialized_user.initial_data['email'],
                serialized_user.initial_data['password']
            )

            tokens = MyTokenObtainPairSerializer(request.data).validate(request.data)
            return Response(tokens, status=status.HTTP_201_CREATED)
        else:
            return Response(serialized_user._errors, status=status.HTTP_400_BAD_REQUEST)

I have try code from @ckeeney and then i have case , response become
[
"No active account found with the given credentials"
]

I think, that code must use table User Auth, becaus I use custom table user to this code.. how can i Fix that ? should i use custom JWT for this case ? thanks

Hi,
here an exemple with a custom user model from sqlalchemy
As recommended in the doc, I used the JWTTokenUserAuthentication authentication class

serializer.py

from common.model.shared.User import User
from common.businesslogic.persistance.UserBL import UserBL
from rest_framework import fields
from rest_witchcraft.serializers import ModelSerializer
from rest_framework.validators import UniqueValidator
from common.config.database.connection import ConnectorPostgres, ConnectorPostgresTest
from rest_framework.reverse import reverse as api_reverse
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

from django.utils.six import text_type

userBL = UserBL().connect(conn = ConnectorPostgresTest(), autocommit=False)
session = userBL.session


class SignupSerializer(TokenObtainPairSerializer):
    uri             = fields.SerializerMethodField(read_only=True)
    username        = fields.CharField( validators=[UniqueValidator(queryset=userBL.getAll())] )
    password        = fields.CharField(write_only=True)

    class Meta:
        model = User
        session = session
        fields = [
            'user_id',
            'username',
            'password',
            'uri'
        ]

    @classmethod
    def get_token(cls, user):
        token = super(SignupSerializer, cls).get_token(user)
        # Add custom claims
        #token['username'] = user.username
        # ...
        return token


    def validate(self, attrs):
        user = userBL.getUserAccount(attrs.get('username'), attrs.get('password'))
        data = {}
        data['username'] = user.username
        data['email'] = user.email
        refresh = self.get_token(user)
        data['token'] = text_type(refresh)
        data['refresh'] = text_type(refresh)
        data['access'] = text_type(refresh.access_token)

        return data



    def get_uri(self, obj):
        request = self.context.get('request')
        return api_reverse("api-user:detail", kwargs={"username": obj.username}, request=request)

view.py

from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
    TokenVerifyView
)


from rest_framework import generics, permissions, status
from rest_framework.views import APIView
from rest_framework.response import Response

from .serializers import SignupSerializer, userBL


from common.model.shared.User import User
from common.businesslogic.persistance.UserBL import UserBL

from rest_framework_simplejwt import authentication


class AuthAPIView(TokenObtainPairView):
    authentication_classes  = [authentication.JWTTokenUserAuthentication]
    serializer_class        = SignupSerializer
    permission_classes      = [permissions.AllowAny]
    queryset                = userBL.getAll()


    def post(self, request, *args, **kwargs):
        data = request.data
        login = data.get('username')
        password = data.get('password')

        response={"detail":"Invalid credentials"}

        user = userBL.getUserAccount(login, password)
        if (user != None):
            serializer = self.get_serializer(data=request.data)    
            response = serializer.validate(request.data)
        else:
            response = {"detail":"Invalid credentials"}
            return Response(response, status=status.HTTP_401_UNAUTHORIZED)

        return Response(response, status=status.HTTP_201_CREATED)

This is a copy/past of my prod code so feel free to ask for more details

from rest_framework_simplejwt.tokens import RefreshToken

def get_tokens_for_user(user):
refresh = RefreshToken.for_user(user)

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

The above function get_tokens_for_user will return the serialized representations of new refresh and access tokens for the given user. In general, a token for any subclass of rest_framework_simplejwt.tokens.Token can be created in this way.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mjackstone6 picture mjackstone6  路  4Comments

ethanjahn picture ethanjahn  路  7Comments

mosi-kha picture mosi-kha  路  6Comments

GeoffMahugu picture GeoffMahugu  路  5Comments

laurent-brisbois picture laurent-brisbois  路  3Comments