2

Whenever I am logged into a django user and I try and send a PUT request to my URL I get a 403 Forbidden Error. However, it works both when I am not logged in and also from the Django Rest API client.

Here is my code in my frontend:

let parameters = `user/${userID}/`
    return new Promise((resolve, reject) => {
        axios({
            method: 'PUT',
            url: 'http://127.0.0.1:8000/' + parameters,
            data: updatedUser,
            headers: {
                'Content-Type': 'application/json',
            },
        })
            .then((response) => {
                resolve(response)
            })
            .catch(error => {
                console.log(error)
                // reject(error)
            })
    });

I am very confused as I can't see the difference when I am logged into a django user and when I am not, as nothing changes in the frontend. Thanks

EDIT: This is in my urls.py

 path('user/<id>/', views.RetrieveUpdateDestroyUser.as_view()),

And this is the view:

class RetrieveUpdateDestroyUser(RetrieveUpdateDestroyAPIView):
"""
View to handle the retrieving, updating and destroying of a User.
This View will also log any changes made to the model.
"""
serializer_class = UserCreateUpdateSerializer
queryset = CustomUser.objects.all()
lookup_field = 'id'
permission_classes = (AllowAny,)

def update(self, request, *args, **kwargs):
    """
    PUT and UPDATE requests handled by this method.
    """
    return super().update(request, *args, **kwargs)

I have also tested doing POST and PUT when I am logged into a user and they don't work, but GET does. Thanks

Also tried disabled CSRF but to no avail either

yaza
  • 35
  • 1
  • 6
  • Could you show the code of `/user/` endpoint on Django side? – Dmitry Belaventsev Sep 18 '20 at 15:08
  • And I guess that there are cases when `userID` variable is empty of not defined. So Axios receives wrong value of `url`. – Dmitry Belaventsev Sep 18 '20 at 15:19
  • @DmitryBelaventsev I've made an edit, thanks for helping. I currently am trying to PUT to a fixed userID that exists so unsure – yaza Sep 18 '20 at 15:38
  • I still think it's something about CSRF. Could you try the following before axios request: `axios.defaults.xsrfHeaderName = "X-CSRFTOKEN"; axios.defaults.xsrfCookieName = "csrftoken";`? And how did you try to disable CSRF? – Dmitry Belaventsev Sep 18 '20 at 16:45
  • @DmitryBelaventsev I have both of those along with `axios.defaults.withCredentials = true` in my .js file. To disable CSRF I used a method decorator with csrf_exempt. It's just weird because the axios request works when I'm not logged into any user at all but as soon as I have a user logged in, even admin, it has the 403 Forbidden for PUT and POST. – yaza Sep 18 '20 at 18:23
  • I think you did CSRF disabling in a wrong way. Try the recipe from this answer https://stackoverflow.com/a/30875830/764182 – Dmitry Belaventsev Sep 18 '20 at 18:51
  • CSRF check isn't performed for anonymouse (not logged in) users. That's why it's working for them. – Dmitry Belaventsev Sep 18 '20 at 18:51
  • In the meantime you have tried disabling of csrf before. But most likely you just used @csrf_exempt decorator, which will not work for DRF. – Dmitry Belaventsev Sep 18 '20 at 18:52
  • When it will start working without CSRF, you might want to check this part of Django docs https://docs.djangoproject.com/en/3.1/ref/csrf/#ajax to see what should be done to make AJAX calls with CSRF. As for axios csrf settings - please check that cookie name and header name are the same as I've posted above. – Dmitry Belaventsev Sep 18 '20 at 18:54
  • @DmitryBelaventsev oh wow thank you so much that works now, I literally would've been stuck forever because I thought I tried it with CSRF disabled. You're a legend, cheers. – yaza Sep 18 '20 at 19:14

1 Answers1

2

Writing this answer to summarize what we have discovered.

The problem: the AJAX (PUT) call to the DRF endpoint fails with 403 HTTP error for authenticated users and works just fine for anonymous users

Desired Behaviour: make that call working for both anonymous and authenticated users

Reason: by default DRF perform CSRF check for unsafe HTTP methods (POST, PUT, PATCH and DELETE) https://www.django-rest-framework.org/topics/ajax-csrf-cors/

Possible Solutions:

  1. Disable CSRF check like described here https://stackoverflow.com/a/30875830/764182
  2. Pass CSRF token within the PUT request. For more information about CSRF + AJAX in Django read here https://docs.djangoproject.com/en/3.1/ref/csrf/#ajax. For Axios and default Django settings the solution might be:
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.withCredentials = true;
Dmitry Belaventsev
  • 6,347
  • 12
  • 52
  • 75