jazzband / djangorestframework-simplejwt

A JSON Web Token authentication plugin for the Django REST Framework.
https://django-rest-framework-simplejwt.readthedocs.io/
MIT License
4.02k stars 663 forks source link

self.request.user returning AnonymousUser #562

Open amipedro opened 2 years ago

amipedro commented 2 years ago

Hello, everyone.

I'm trying to build an application with DRF backend and ReactJS frontend. I followed a tutorial on youtube to install and configure simplejwt. I managed to log in and even restrict some parts with PrivateRoute React Hooks.

My problem is when trying to read information on GET request to backend. My self.request.user returns AnonymousUser. I've searched a lot on StackOverflow and GitHub, but I think my knowledge is limited on this matter.

I saw someone recommending to use @api_view or change the permission_classes to IsAuthenticated, but with no success. Is there anything in specific I should configure?

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',  # new
    'corsheaders',  # new
    'woof',  # new
    # check the database for expired tokens
    'rest_framework_simplejwt.token_blacklist',  # new
]

# Make Django use simplejwt for authentication
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    )
}

# Simple JWT config

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    # Changed from 1 to 90 days, so user doesn't have to login everyday
    'REFRESH_TOKEN_LIFETIME': timedelta(days=90),
    # changed from False to True
    'ROTATE_REFRESH_TOKENS': True,
    # changed frm False to True
    'BLACKLIST_AFTER_ROTATION': True,
    'UPDATE_LAST_LOGIN': False,

    'ALGORITHM': 'HS256',
    # 'SIGNING_KEY': settings.SECRET_KEY,
    'VERIFYING_KEY': None,
    'AUDIENCE': None,
    'ISSUER': None,
    'JWK_URL': None,
    'LEEWAY': 0,

    'AUTH_HEADER_TYPES': ('Bearer',),
    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',
    'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',

    '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),
}

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',  # new
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'django_react.urls'

view.py I'm trying to debug

class FollowingWoofListAPIView(generics.ListCreateAPIView):

    serializer_class = WoofSerializer
    permission_classes = [permissions.AllowAny]

    def get_queryset(self):
        print(self.request)
        print(self.request.user)
        print(type(self.request.authenticators))
        print(self.request.authenticators)
        return Woof.objects.all()
Andrew-Chen-Wang commented 2 years ago

the problem is you're not logged which is why you're getting AnonymousUser. The tutorial most likely had you use a cookie to set the tokens. You may have just missed that. The SimpleJWT organization has an example react repo you can try

amipedro commented 2 years ago

Sounds like so @Andrew-Chen-Wang, the authentication tokens are stored in local storage as access and refresh key. I'll try your solution and give a feedback of what happens. Thank you so much.

spinus commented 2 years ago

If I understand that correctly, JWT is alternative to cookie, so why would you require cookie at all for authenticating with JWT?

I can confirm that with simplejwt the request.user object is not set properly.

Andrew-Chen-Wang commented 2 years ago

no JWT is mostly for stateless auth. Stateless just means all secure details are stored in the token whereas state ful is stored serverside. In django, the default is the cache and what's stored in a cookie by default is a session key (ie the key in the cache)

Storing JWTs in cookies is just a means for transporting the JWT. Just like how we store a cryptographic "key" in a cookie for django by django's session defaults.

So cookies are a means of sending a key/auth stuff between client and server.

spinus commented 2 years ago

Right, I get your point, but because of that approach, looks like API behavior is not consistent. My understanding is that authentication is abstracted by DRF/django and there is a promise that request.user is the thing. Looks like this API breaks Authentication abstraction provided by django/drf (maybe even that abstraction has no point anymore in that specific use case) as user/dev needs to manually handle that code in views (or add other custom/glue/hack somewhere).

To your point, if JWT transfers the user info, it should recreate that user and keep API promise (or at least set it to something very different than AnonymousUser, I guess maybe even to either Proxy object, or something to throw an exception to say to user this case is not handled by this authentication method, instead of pretending all is OK).

Andrew-Chen-Wang commented 2 years ago

When it comes to SPAs, yes, you need to program some views for proper cookie stuff. In the case of OP, the permission for "Logged in" is not set, so AnonymousUser is returned as expected. Sorry if I'm not understanding.

yznts commented 2 years ago

As I see, request.user is resolving only on requiring authentication on permissions level (F.e. with IsAuthenticated). So, AllowAny is not working, unlike another types of auth. I need it, as well as couple of another devs. Use case: It happens routes are having different behavior depending on auth state (f.e. scoping queryset). We need to provide AllowAny, but modify behavior according to request.user. As a result, this JWT module is not working for us.

UPD: I've provided wrong auth schema in token ("Authorization: JWT ..."), django autodoc is quite misleading in terms of token authorization: Either a registered authentication scheme such as Bearer, or a custom schema such as Token or JWT. At least, it's configurable parameter: AUTH_HEADER_TYPES

Kulwa-silya commented 1 year ago

Hello guys, I'm facing a familiar issue, I'm supposed to track the user activities in a Django API, I have created a custom middleware to check every request but JWTauthentication does not populate the request.user object attribute hence I get AnonymousUser, but when i access the django admin it works because request.user is populated by the 'django.contrib.auth.middleware.AuthenticationMiddleware' ,anybody with any idea how I can solve it please:

_____here is my middleware.

from .models import UserActivity from django.utils import timezone

class UserActivityMiddleware: def init(self, get_response): self.get_response = get_response

def __call__(self, request):
    # Code to be executed for each request before
    # the view (and later middleware) are called.

    # Log the user's activity
    if request.user.is_authenticated:
        user_activity = UserActivity(
            user=request.user,
            path=request.path,
            method=request.method,
            form_data=request.POST,
            timestamp=timezone.now()
        )
        user_activity.save()

    response = self.get_response(request)

    # Code to be executed for each request/response after
    # the view is called.

    return response

_____here is my settings.

REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated'] }

SIMPLE_JWT = { 'AUTH_HEADER_TYPES': ('JWT',), 'ACCESS_TOKEN_LIFETIME': timedelta(days=3), }

AlexTo commented 1 year ago

I'm having the same issue. I'm able to get the User object in the View but not in the middleware.

The Authorization header is present (well, obviously because I can get the User object at later state).

I think I can add the logic to populate the request.user in my custom middleware but is it the proper way to do so. I'm think some other existing middleware already did that.

Any idea would be very much appreciated. Thanks

image
bonface221 commented 1 year ago

Same issue here

trying to get the user in the middleware results in an anonymous user when the user is logging in!!

image

this is strange

liq2023 commented 1 year ago

when it is the right token but self.request.user is anonymousUser ,throttle will count this user as an anon,what shoul be done to make it right?

martin-thoma commented 3 months ago

Did anybody find a work-around for this?

@liq2023 How did you deal with this issue?