Django rest framework with simplejwt getting a 200 ok response even for invalid tokens
I am using django-rest-framework, djoser and simplejwt to build out token auth for user accounts.
I can create an account, activate and login with Postman. But for some reason I having an issue with the /refresh and /verify endpoints. I'm also having an issue with /logout and /auth/me but I'm guessing if I can fix the /refresh and /verify endpoints the other ones will also fix themselves.
These are the test urls and methods that I'm using with Postman
POST http://localhost:8000/api/users/
After this I go to my email and copy the uid and token
POST http://localhost:8000/api/users/activation/
enter the uid and token in the body of the request
POST http://localhost:8000/api/jwt/create/
make sure email and pw are in the body and click send, receive refresh and access tokens
POST http://localhost:8000/api/jwt/refresh/
enter the refresh token like so {"refresh": "kjlsjfdlskldjfls...."}
It is here that I will enter an extra character at the end to make it and invalid token, but I still get a 200 ok along with an access token, why?
POST http://localhost:8000/api/jwt/verify
enter {"token": "jaslkfjsld...."}
again, enter an extra character to invalidate the token but I still get a 200 ok with an access token, why?
I have poured over what I believe to be the relevant code and can not see where I am going wrong?
This is my custom User model with DRF:
class UserAccount(AbstractBaseUser, PermissionsMixin): **model.py**
id = f'{models.UUIDField(primary_key=True, default=RandomUUID, editable=False)}'
first_name = models.CharField(blank=True, max_length=255)
last_name = models.CharField(blank=True, max_length=255)
email = models.EmailField(unique=True, max_length=255)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)
objects = UserAccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name']
def __str__(self):
return self.email
This is my UserAccountManager: manager.py
class UserAccountManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
if not email:
raise ValueError("Users must have an email address")
email = self.normalize_email(email)
email = email.lower()
user = self.model(
email=email,
**kwargs
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None, **kwargs):
"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
user = self.create_user(
email,
password=password,
**kwargs
)
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
These are my User views, views.py
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
TokenVerifyView
)
class CustomTokenObtainPairView(TokenObtainPairView):
def post(self, request, *args, **kwargs):
response = super().post(request, *args, **kwargs)
if response.status_code == 200:
access_token = response.data.get('access')
refresh_token = response.data.get('refresh')
response.set_cookie(
'access',
access_token,
max_age=settings.AUTH_COOKIE_MAX_AGE,
path=settings.AUTH_COOKIE_PATH,
secure=settings.AUTH_COOKIE_SECURE,
httponly=settings.AUTH_COOKIE_HTTP_ONLY,
samesite=settings.AUTH_COOKIE_SAMESITE
)
response.set_cookie(
'refresh',
refresh_token,
max_age=settings.AUTH_COOKIE_MAX_AGE,
path=settings.AUTH_COOKIE_PATH,
secure=settings.AUTH_COOKIE_SECURE,
httponly=settings.AUTH_COOKIE_HTTP_ONLY,
samesite=settings.AUTH_COOKIE_SAMESITE
)
return response
class CustomTokenRefreshView(TokenRefreshView):
def post(self, request, *args, **kwargs):
refresh_token = request.COOKIES.get('refresh')
if refresh_token:
request.data['refresh'] = refresh_token
response = super().post(request, *args, **kwargs)
if response.status_code == 200:
access_token = response.data.get('access')
response.set_cookie(
'access',
access_token,
max_age=settings.AUTH_COOKIE_MAX_AGE,
path=settings.AUTH_COOKIE_PATH,
secure=settings.AUTH_COOKIE_SECURE,
httponly=settings.AUTH_COOKIE_HTTP_ONLY,
samesite=settings.AUTH_COOKIE_SAMESITE
)
return response
class CustomTokenVerifyView(TokenVerifyView):
def post(self, request, *args, **kwargs):
access_token = request.COOKIES.get('access')
if access_token:
request.data['token'] = access_token
return super().post(request, *args, **kwargs)
class LogoutView(APIView):
def post(self, request, *args, **kwargs):
response = Response(status=status.HTTP_204_NO_CONTENT)
response.delete_cookie('access')
response.delete_cookie('refresh')
return response
These are the urls for create, refresh, verify and logout
urlpatterns = [
path('jwt/create/', CustomTokenObtainPairView.as_view()),
path('jwt/refresh/', CustomTokenRefreshView.as_view()),
path('jwt/verify/', CustomTokenVerifyView.as_view()),
path('logout/', LogoutView.as_view()),
]
And this authentication.py is used for httponly cookies mgmt:
from django.conf import settings
from rest_framework_simplejwt.authentication import JWTAuthentication
class CustomJWTAuthentication(JWTAuthentication):
def authenticate(self, request):
try:
header = self.get_header(request)
if header is None:
raw_token = request.COOKIES.get(settings.AUTH_COOKIE)
else:
raw_token = self.get_raw_token(header)
if raw_token is None:
return None
validated_token = self.get_validated_token(raw_token)
return self.get_user(validated_token), validated_token
except:
return None