Django-rest-framework returns 403 response to POST, PUT, DELETE, despite AllowAny permissions

I use django-oneall to enable authentication of a social login session on my site. Although it is not one of the suggested auth providers for the django-rest-framework, rest_framework.authentication.SessionAuthentication uses django's default session authentication. so I thought it should be pretty easy to integrate.

On the permissions side, I will ultimately use IsAdmin , but for development purposes, I just set the value to IsAuthenticated . When this returned 403s, I softened permissions on AllowAny , but there are still no dice. Here is my rest framework configuration:

settings.py

 REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.AllowAny', # 'rest_framework.permissions.IsAuthenticated', # 'rest_framework.permissions.IsAdminUser', ), 'PAGE_SIZE': 100, 'DEFAULT_FILTER_BACKENDS': ( 'rest_framework.filters.DjangoFilterBackend', ), } 

EDIT:

I got this job based on the answer below. It turns out that rest_framework expects both a csrftoken cookie and a X-CSRFToken Header with the same value, I configure my front end code to send this header for all ajax requests, and everything works fine.

+8
source share
3 answers

The Django REST Framework returns a 403 status code in several cases:

  • If you do not have the required permission level (for example, if the API request is not authenticated, if DEFAULT_PERMISSION_CLASSES - ('rest_framework.permissions.IsAuthenticated',) .
  • When you execute an unsafe type of request (POST, PUT, PATCH or DELETE request, which should have side effects), you use rest_framework.authentication.SessionAuthentication , and you did not include CSRFToken in the request.
  • When you execute an unsafe type of request and the CSRFToken that you turned on is no longer valid.

I am going to make some demo requests against the test API to give an example of each of them to help you diagnose what problem you have and show how to solve it. I will use the requests library.

Test API

I installed a very simple DRF API with one model, Life , which contains one field ( answer with a default value of 42 ). Everything that happens here is fairly straightforward; I installed ModelSerializer - LifeSerializer , a ModelViewSet - LifeViewSet and a DefaultRouter on the URL /life route. I configured DRF to require user authentication to use the API and use SessionAuthentication .

API enable

 import json import requests response = requests.get('http://localhost:8000/life/1/') # prints (403, '{"detail":"Authentication credentials were not provided."}') print response.status_code, response.content my_session_id = 'mph3eugf0gh5hyzc8glvrt79r2sd6xu6' cookies = {} cookies['sessionid'] = my_session_id response = requests.get('http://localhost:8000/life/1/', cookies=cookies) # prints (200, '{"id":1,"answer":42}') print response.status_code, response.content data = json.dumps({'answer': 24}) headers = {'content-type': 'application/json'} response = requests.put('http://localhost:8000/life/1/', data=data, headers=headers, cookies=cookies) # prints (403, '{"detail":"CSRF Failed: CSRF cookie not set."}') print response.status_code, response.content # Let grab a valid csrftoken html_response = requests.get('http://localhost:8000/life/1/', headers={'accept': 'text/html'}, cookies=cookies) cookies['csrftoken'] = html_response.cookies['csrftoken'] response = requests.put('http://localhost:8000/life/1/', data=data, headers=headers, cookies=cookies) # prints (403, '{"detail":"CSRF Failed: CSRF token missing or incorrect."}') print response.status_code, response.content headers['X-CSRFToken'] = cookies['csrftoken'] response = requests.put('http://localhost:8000/life/1/', data=data, headers=headers, cookies=cookies) # prints (200, '{"id":1,"answer":24}') print response.status_code, response.content 
+22
source

For completeness, there is another circumstance in which DRF returns the 403 code: if you forget to add as_view() to the view declaration in your urls.py file. It just happened to me, and I spent hours until I figured out where the problem is, so maybe this add-on can save some time for someone.

+1
source

Just for those who might find the same problem. If you use viewsets without routers, such as:

 user_list = UserViewSet.as_view({'get': 'list'}) user_detail = UserViewSet.as_view({'get': 'retrieve'}) 

The Django Rest framework will return 403 if you do not define allow_classes at the class level:

 class UserViewSet(viewsets.ModelViewSet): """ A viewset for viewing and editing user instances. """ permission_classes= YourPermisionClass 

Hope it helps!

0
source

All Articles