Reputation: 891
I'm using a django-oneall to allow social login session authentication on my site. While it isn't one of the suggested auth providers for django-rest-framework, rest_framework.authentication.SessionAuthentication
uses django's default session authentication. so I thought it should be fairly simple to integrate.
On the permissions side, ultimately I'll use IsAdmin
, but for development purposes, I just had it set to IsAuthenticated
. When that returning 403s, I relaxed the permissions to AllowAny
, but still no dice. Here's my rest framework config:
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 working based on the answer below. It turns out that rest_framework
expects both the csrftoken
cookie and a a X-CSRFToken
Header of the same value, I setup my front-end code to send that header for all ajax requests and everything worked fine.
Upvotes: 22
Views: 40160
Reputation: 1214
If anyone facing similar kind of issue after migrating from Django 3 to Django 4 then it might be because of misconfigured CSRF_TRUSTED_ORIGINS
in settings file.
One possible cause will be:
In Django 3, the list of CSRF_TRUSTED_ORIGINS
contains only domains without protocol.
CSRF_TRUSTED_ORIGINS = ['example.com']
But in Django 4, the list of CSRF_TRUSTED_ORIGINS
should have protocol defined.
CSRF_TRUSTED_ORIGINS = ['https://example.com']
Ref: https://docs.djangoproject.com/en/4.2/ref/settings/#csrf-trusted-origins
Upvotes: 2
Reputation: 1
use @csrf_exempt at the top of your POST function from django.views.decorators.csrf import csrf_exempt
example
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def student_api(request):
request. MethodMethod == 'POST':
json_data = request. Body
stream = io.BytesIO(json_data)
pythondata = JSONParser().parse(stream)
serializer = StudentSerializer(data=pythondata)
if serializer.is_valid():
serializer.save()
res = {'msg': 'Data Created'}
json_data = JSONRenderer().render(res)
return HttpResponse(json_data, content_type='application/json')
json_data = JSONRenderer().render(serializer.errors)
return HttpResponse(json_data, content_type='application/json')
Upvotes: 0
Reputation: 1
One more situation that someone may find is that you get a 403 error on an AllowAny route when you pass an token as null in the "Authorization" header in your request. For example, you may want to allow anyone to use the route but also want to know if the person that used the route is an authenticated user.
E.g.
if (token) {
headers = {
"Content-Type": "application/json",
"Authorization": "Token " + token
}
} else {
headers = {
"Content-Type": "application/json"
}
}
Upvotes: 0
Reputation: 3739
For those that aren't able to even access their csrftoken from Javascript:
In my case I wasn't able to get the csrftoken from my Javascript code to be able to set it in my ajax POST. It always printed null. I finally discovered that the django CSRF_COOKIE_HTTPONLY environment variable was set to True.
From the Django Documentation
CSRF_COOKIE_HTTPONLY: If this is set to True, client-side JavaScript will not be able to access the CSRF cookie."
Changing CSRF_COOKIE_HTTPONLY to False allowed me to finally get the csrftoken.
https://docs.djangoproject.com/en/4.1/ref/settings/#std-setting-CSRF_COOKIE_HTTPONLY
Upvotes: 1
Reputation: 1036
Just for anyone that might find the same problem. If you are using viewsets without routers like:
user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})
Django Rest framework will return 403 unless you define permission_classes at a class level:
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset for viewing and editing user instances.
"""
permission_classes= YourPermisionClass
Hope it helps!
Upvotes: 4
Reputation: 63
For completeness sake, there is one more circumstance under which DRF returns code 403: if you forget to add as_view()
to the view declaration in your urls.py file. Just happened to me, and I spent hours until I found where the issue was, so maybe this addition can save some time for someone.
Upvotes: 2
Reputation: 5997
Django REST Framework returns status code 403
under a couple of relevant circumstances:
DEFAULT_PERMISSION_CLASSES
is ('rest_framework.permissions.IsAuthenticated',)
.rest_framework.authentication.SessionAuthentication
and you've not included your CSRFToken in the requeset.I'm going to make a few demo requests against a test API to give an example of each to help you diagnose which issue you are having and show how to resolve it. I'll be using the requests
library.
The test API
I set up a very simple DRF API with a single model, Life
, that contains a single field (answer
, with a default value of 42
). Everything from here on out is pretty straight forward; I set up a ModelSerializer
- LifeSerializer
, a ModelViewSet
- LifeViewSet
, and a DefaultRouter
on the /life
URL route. I've configured DRF to require user's be authenticated to use the API and to use SessionAuthentication
.
Hitting the API
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's 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
Upvotes: 51