Accessing logged in user name inside django middleware

801 Views Asked by At

I am trying to access request.user.username inside following django middleware:

class MyMiddleware():
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        user_id = request.user.username
        # ... 

I ensured that MyMiddleware appears after SessionMiddleware and AuthenticationMiddleware in the settngs.py:

MIDDLEWARE = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    # ...
    `my_app.my_py_file.MyMiddleware`
)

But still the request.user.username turns out to be an empty string.

Q1. Why is it so?

I also tried adding @authentication_classes([TokenAuthentication,]):

from rest_framework.authentication import TokenAuthentication # <--
from rest_framework.decorators import authentication_classes # <--

@authentication_classes([TokenAuthentication,]) # <--
class MyMiddleware():
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        user_id = request.user.username
        # ... 

But no help.

I also tried making class APIView (though it looks stupid as middleware is not a REST API):

from rest_framework.authentication import TokenAuthentication # <--
from rest_framework.views import APIView # <--

class MyMiddleware(APIView): # <-- 
    authentication_classes = [TokenAuthentication] # <--
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        user_id = request.user.username
        # ... 

Still, it did not work.

Then I came across this answer. It says:

If you use simple_jwt, it's not possible to directly use request.user in a middleware because authentication mechanism works in view function. So you should manually authenticate inside the middleware and then you can use request.user to get whatever data from the user model.

It makes use of rest_framework_simplejwt which does not seem to be the part of django framework and needs to be installed explicitly.

The only way I am able to access username in my middleware is using this answer. I modified my middleware as follows:

from re import sub
from rest_framework.authtoken.models import Token

class MyMiddleware(object):

    def __init__(self, get_response):
        self.get_response = get_response


    def __call__(self, request):
        response = self.get_response(request)
        user_id = request.user.username

        if not user_id: 
            user_id = self.getUsername(request)
        
        # ...
    
    def getUsername(self, request):
        header_token = request.META.get('HTTP_AUTHORIZATION', None)
        if header_token is not None:
            try:
                token = sub('Token ', '', header_token)
                token_obj = Token.objects.get(key = token)
            except Token.DoesNotExist:
                pass
        return token_obj.user.username

Q2. Is this the only and correct way to access username inside middleware without installing third party libraries?

Update

Below is my CustomUser class and possibly related method create_custom_user():

class CustomUser(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    # there are some application specific fields    

@receiver(post_save, sender=User)
@debugger_queries
def create_custom_user(sender, **kwargs):
    instance = kwargs['instance']
    if kwargs['created']:  # create
        CustomUser.objects.get_or_create(user=instance)
1

There are 1 best solutions below

0
Alexandr Zayets On

Accordingly to you answer here Accessing logged in user name inside django middleware - AnonymousUser in the request user mean that at the moment when you checking it user is not authenticated. You can check code for anonymous user here - https://github.com/django/django/blob/main/django/contrib/auth/models.py#L417 . There's indeed an empty string for username.

I assume you are using django rest framework, so i answer based on that

Depending on which auth classes(https://www.django-rest-framework.org/api-guide/authentication/#setting-the-authentication-scheme) you are using for rest framework authentication may go in different ways. For rest_framework.authentication.SessionAuthentication rest framework will get user from request. In that case AuthenticationMiddleware will assign correct user to request object and drf will have access.

For other auth types from drf such as TokenAuthenticaton session will not contain any info about user, because user will be authenticated via token passed in http header. In such cases you will not be able to access user in the middleware before auth class did it's job.

So possible solutions for you is either authenticate user manually with in middleware using your auth class like

user, token = TokenAuthentication().authenticate(request)

but be careful with that, because middleware will be applied for all requests, so you have to figure out how to not django admin or just regular pages if you use some

Alternatively you can place your logic somewhere else and not use middleware, depending on what you want to achive