I know it's not the first question of that kind, but other topics didn't help me. I want to use djangorestframework-simplejwt to authenticate user and everything is fine since it comes to getting user. I always get AnonymousUser.
I obtain token by visiting api/token and passing credentials. Token is created correctly, it contains right user id. When I try to get access to protected view it allows me to, but Django doesn't recognise user.
settings.py
import os
from datetime import timedelta
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ["SECRET_KEY"]
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"rest_framework",
"corsheaders",
"djmoney",
"bills",
"accounts",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "shared_bills.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
]
},
}
]
WSGI_APPLICATION = "shared_bills.wsgi.application"
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
}
# Authentication
AUTH_USER_MODEL = "accounts.User"
# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
},
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
]
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = "/static/"
APPEND_SLASH = True
# CORS
CORS_ORIGIN_ALLOW_ALL = True
# Rest Framework
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
"DEFAULT_RENDERER_CLASSES": ["rest_framework.renderers.JSONRenderer"],
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTTokenUserAuthentication"
],
}
if DEBUG:
REST_FRAMEWORK["DEFAULT_RENDERER_CLASSES"].append(
"rest_framework.renderers.BrowsableAPIRenderer"
)
REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"].extend(
[
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.BasicAuthentication",
]
)
# Simple JWT
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(weeks=1),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': False,
'ALGORITHM': 'HS256',
'SIGNING_KEY': os.environ["SECRET_KEY"],
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
views.py """Views for accounts application."""
from django.contrib.auth import get_user
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin
from rest_framework.permissions import BasePermission, IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from accounts.models import User
from accounts.serializers import (
PasswordCheckSerializer,
UserChangePasswordSerializer,
UserSerializer,
)
class IsAnonymous(BasePermission):
"""Allows access only to anonymous users."""
def has_permission(self, request, view):
return not bool(request.user and request.user.is_authenticated)
class UserViewset(GenericViewSet, CreateModelMixin, RetrieveModelMixin):
"""Viewset for User object."""
queryset = User.objects.all()
def get_serializer_class(self):
if self.action == "register":
return UserSerializer
if self.action == "change_password":
return UserChangePasswordSerializer
if self.action == "delete_user":
return PasswordCheckSerializer
return UserSerializer
def get_object(self):
return get_user(self.request)
@action(detail=True, methods=["post"], permission_classes=[IsAnonymous])
def register(self, request, *args, **kwargs):
"""Registering new User."""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
@action(detail=True, methods=["post"], permission_classes=[IsAuthenticated])
def change_password(self, request, *args, **kwargs):
"""Changes password."""
user = self.get_object()
serializer = self.get_serializer(user, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({"detail": "password changed"}, status=status.HTTP_200_OK)
@action(detail=True, methods=["post"], permission_classes=[IsAuthenticated])
def delete_user(self, request, *args, **kwargs):
"""Deletes user."""
user = self.get_object()
serializer = self.get_serializer(user, data=request.data)
serializer.is_valid(raise_exception=True)
user.delete()
return Response({"detail": "user deleted"}, status=status.HTTP_200_OK)
I believe it's something obvious, couse I started learning drf few days ago but hours of searching google did not helped.
First you should use self.request.user
instead of get_user
. The latter assumes session-based authentication and is a function used by the session auth backend to add the user to the request object. Regardless of the auth backend, the middleware takes care of fetching the user for you and adds it to the request object.
Also you’re using JWTTokenUserAuthentication
instead of JWTAuthentication
. The latter would return you a User
object in your request, whereas the former puts a TokenUser
in the request.
See this explanation.