Been-H
Been-H

Reputation: 87

First django rest api call returns request.user as AnonymousUser but further calls return proper user

right now I have an api view that requires knowing which user is currently logged in. When I try to call it with a logged in user; however, it returns anonymous user. The odd thing is that if I call the api view again, it returns the proper user. I am using django rest framework and JSON Web Tokens with simplejwt. Right now the call in ReactJS looks like this:

const fetchAuthorData = () => {
        axiosInstance.get('authors/')
        .then((res) => {
            setAuthor(res.data)
        }).catch((err) => {
            fetchAuthorData()
        })
        
        fetchAuthorThreads()
        fetchAuthorArticles()
    }

However, I know recursively calling this over and over is very dangerous, because even though I redirect if the user is actually not logged in before this recursive api call, if somehow that fails my server will be overloaded with api calls. Does anyone know how I could make it so that my api actually properly identifies on the first call whether the user is logged in?

Here is my login view:

axiosInstance.post('author-auth/token/', {
                email: formData.email,
                password: formData.password,
            }).then((response) => {
                localStorage.setItem('access_token', response.data.access)
                localStorage.setItem('refresh_token', response.data.refresh)
                axiosInstance.defaults.headers['Authorization'] = 
                    'JWT ' + localStorage.getItem('access_token')
                    history.push('/')
            }).catch((err) => {
                alert("Make Sure Login Credentials Are Correct")
            })

Here is the faulty view:

@api_view(['GET'])
def author(request):
    print(request.user)
    if request.user.is_authenticated:
        author = request.user
        serializer = AuthorAccessSerializer(author, many=False)
        return Response(serializer.data)
    else:
        return HttpResponse(status=400)

In this, request.user is an anonymous user even though I am logged in

Here is a similar part of another view that works oddly

@api_view(['GET', 'POST'])
def articles(request):
    if request.method == 'GET':
        category_name = request.query_params['category']
        dash = request.query_params['dash']
        #this part is very similiar and works for some reason
        if dash == "True":
            if request.user.is_authenticated:
                articles = Article.objects.filter(author=request.user)
            else:
                return HttpResponse(status=401)
        # *end part*
        elif category_name:
            try:
                category = Category.objects.get(name__icontains=category_name)
            except:
                return HttpResponse(status=400, content="Category not found")
            articles = Article.objects.filter(category=category)
        else:
            articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)
        
        return Response(serializer.data)

Here are my axios settings:

export const axiosInstance = axios.create({
    baseURL: baseURL,
    timeout: 5000,
    headers : {
        Authorization: localStorage.getItem('access_token')
            ? 'JWT' + localStorage.getItem('access_token')
            : null,
        'Content-Type' : 'application/json',
        accept : 'application/json'
    }
})

And here are my rest framework and JWT auth settings:

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

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(hours=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    'ROTATE_REFRESH_TOKENS': False,
    'BLACKLIST_AFTER_ROTATION': True,
    'UPDATE_LAST_LOGIN': False,

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

    'AUTH_HEADER_TYPES': ('Bearer', 'JWT'),
    '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),
}

ALLOWED_HOSTS=['127.0.0.1', 'http://localhost:5000']
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = (
    'http://localhost:3000',
) 

Again, if I keep calling the same api call, it works for some reason not changing any authorization headers, tokens, or anything else and request.user works fine with the second view I have above. Can anyone tell me why this is happening?

Upvotes: 1

Views: 968

Answers (1)

rumbarum
rumbarum

Reputation: 983

I think,

                1. localStorage.setItem('access_token', response.data.access)
                2. localStorage.setItem('refresh_token', response.data.refresh)
                3. axiosInstance.defaults.headers['Authorization'] = 
                    'JWT ' + localStorage.getItem('access_token')
                    history.push('/')

on login code may cause problem.

These code never run sequentially; every javascript run asynchronously.

It means, your axiosInstance.defaults.headers could be null because, 1, 2 and 3 lines are executed at once. In other words, axiosInstance.defaults.headers become null (as there is no access_token on localStorage) then, Js set access_token from response.data.access.

You'd better use promise with .then() after 1 and 2 line, or fetch Authorization of axiosInstance when make http request.

Upvotes: 1

Related Questions