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.
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.
Most helpful comment
I have this working reasonably well, but the code could probably be a bit cleaner.
If I post a
usernameandpasswordto this view, aUseris created and the response contains my access tokens with a claim added for the user's email address.