I created a Django app in which I want to be able to authenticate users by checking not only the username and password, but also a specific field in a related model. The custom request body I want to POST
to the endpoint is:
payload = { 'username': user, 'password': password, 'app_id': uuid4}
I am using djangorestframework-simplejwt
module to get the access token.
models.py
class Application(models.Model):
app_name = models.CharField(max_length=300)
app_id = models.UUIDField(default=uuid.uuid4, editable=False)
def __str__(self):
return self.app_name
class ProfileApp(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
app = models.ForeignKey(Application, on_delete=models.CASCADE)
expires_on = models.DateTimeField(default=datetime.now() + timedelta(days=15))
def __str__(self):
return self.app.app_name + " | " + self.user.username
Is it possible to override the TokenObtainPairView
from rest_framework_simplejwt
to only authenticate an user if the expires_on
date is still not expired? Or is there an architecture problem by doing it like this?
You can do this by creating a custom serializer that inherits from TokenObtainPairSerializer
, and extending the validate
method to check for custom field values. There is no architecture problem if you are careful to not override necessary functionality of the parent class.
Here is an example:
import datetime as dt
import json
from rest_framework import exceptions
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
try:
request = self.context["request"]
except KeyError:
pass
else:
request_data = json.loads(request.body)
username = request_data.get("username")
app_id = request_data.get("app_id")
profile_has_expired = False
try:
profile = ProfileApp.objects.get(user__username=username, app__app_id=app_id)
except ProfileApp.DoesNotExist:
profile_has_expired = True
else:
profile_has_expired = dt.date.today() > profile.expires_on
finally:
if profile_has_expired:
error_message = "This profile has expired"
error_name = "expired_profile"
raise exceptions.AuthenticationFailed(error_message, error_name)
finally:
return super().validate(attrs)
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
Then use MyTokenObtainPairView
in place of TokenObtainPairView
in your urls file.
Also since User
and ProfileApp
share a one-to-one field, it looks like you can get away with not using the "app_id
" key/field at all.
Original source file: https://github.com/davesque/django-rest-framework-simplejwt/blob/04376b0305e8e2fda257b08e507ccf511359d04a/rest_framework_simplejwt/serializers.py
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With