django-rest-framwork UpdateView, DeleteView occurs csrf token error? - django

I'm using django-rest-framework and implement very simple post CRUD API.
But the problem is UpdateView and DeleteView occurs csrf error
"detail": "CSRF Failed: CSRF token missing or incorrect."
Strange thing is CreateView doens't require csrf and works very well.
Here is my view and serializer
views.py
class PostEditAPIView(RetrieveUpdateAPIView):
"""
http://example.com/posts/1/edit
"""
queryset = Post.objects.all()
serializer_class = PostUpdateSerializer
lookup_url_kwarg = 'post_id'
serializer.py
class PostUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = [
"title",
"content",
]
I think this is enough for source code.
After clicked PUT button,
How can I deal with csrf in API?
I didn't touch any SETTINGS about Rest-framework

One of the solutions I like to use is to forcefully remove any and all kinds of CSRF checks by instructing Django to do so in the middlewares level.
To do this, create a new middleware class which contains the code to disable CSRF checks, and add the middle ware to your existing list of middlewares to run. Make sure you add your custom middleware AFTER the default authentication middleware being used by Django (Order of middlewares in settings matters).
class CSRFDisablerMiddleware(object):
def process_request(self, request):
setattr(request, '_dont_enforce_csrf_checks', True)
Beware I safely use this method since my app is protected using other means of authentication. I am not sure about how you would go about solving this problem if your project needs CSRF authentication in certain areas and not in others. The method described above will disable CSRF authentication throughout your entire Django project.

Related

Django Rest Framework not accepting JWT Authentication Token

I am using the Django Restful API Framework together with Simple JWT and have successfully created a URL for receiving and refreshing a user token.
In order to try out the authentication using the token, I have created a view that simply lists all the posts inside the database. I have then assigned the IsAuthenticated class to the view.
As expected, I get an error message saying that the authentication credentials were not provided. I then went ahead and made a simple GET request using Postman, with the authentication token provided in the "Authorization" tab. The type was set to "Bear Token". Unfortunately, I still get the message "Authentication credentials were not provided." with a 403 Forbidden code.
I have also tried to provide the token in the Headers, as well as make CURL requests, everything to no avail.
My view looks like this:
class PostListView(generics.ListAPIView):
permission_classes = (IsAuthenticated,)
queryset = Post.objects.filter()
This is the serializer:
class PostListSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('__all__')
The settings.py of the Django project:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.AllowAny'],
'DEFAULT_AUTHENTICATION_CLASSES:': ('rest_framework_simplejwt.authentication.JWTAuthentication',)
}
CORS_ALLOW_ALL_ORIGINS = True # For testing purposes
I have followed several different tutorials online, read through numerous posts as well as followed the official documentation of Simple JWT.
Well what you are doing is trying to filter the data while your basic purpose is to just list your model. For filtering make sure your go through the documentation DRF Filtering.
Try these changes in your code. I hope it will work for you.
Settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
Views.py
class UserList(generics.ListAPIView):
permission_classes = (IsAuthenticated,)
queryset = Post.objects.all()
serializer_class = PostListSerializer
After this try to hit your API with access token. To learn more about generic views you can go through this link Generic Views in DRF.

csrf_exempt set but CSRF Failed: Referer checking failed - no Referer

I have a backend API, it's in django and deployed on Google Endpoint.
I have a post request that insert data to my DB.
I created a script to use this endpoint but I got this error:
{"detail":"CSRF Failed: Referer checking failed - no Referer."}
Regarding over posts I added the crsf_exempt decorator to my class but it did not change.
I try to add the decorator two ways:
class AddUser(APIView):
""" Create user and company from csv """
#method_decorator(csrf_exempt)
def post(self, request):
#method_decorator(csrf_exempt, name='dispatch')
class AddUser(APIView):
""" Create user and company from csv """
def post(self, request):
But both failed.
This is how I contact my endpoint:
resp = requests.request(
method, url,
headers={'Authorization': 'Bearer {}'.format(
open_id_connect_token)}, **kwargs)
Any ideas ?
Thanks
EDIT
So I tried to add authentication classes to my views but it appears to be a bad idea. This is being real trouble for me.
I tried to get the csrftoken doing like this:
client = requests.session()
# Retrieve the CSRF token first
client.get(url) # sets cookie
print(client.cookies)
if 'csrftoken' in client.cookies:
# Django 1.6 and up
csrftoken = client.cookies['csrftoken']
else:
# older versions
csrftoken = client.cookies
Thing is, I am using IAP to protect my API and I do not have any csrftoken cookie but I do have a something looking like this:
<RequestsCookieJar[<Cookie GCP_IAP_XSRF_NONCE_Q0sNuY-M83380ypJogZscg=1
for ...
How can I use this to make post request to my API ?
So this happened to me because I did not set any authentication_classes to my generic view.
When this option is not set Django automatically use the SessionBackend, which need the csrf token.
I fixed it by adding this to my view: authentication_classes = [ModelBackend, GoogleOAuth2]
#Kimor - Can you try doing this in your urls.py
from django.views.decorators.csrf import csrf_exempt
url('^test/$', csrf_exempt(views.TestView.as_view())),
The get and post methods defined on the APIView class just tell DRF how the actual view should behave, but the view method that the Django router expects is not actually instantiated until you call TestView.as_view().
source
Django REST Framework CSRF Failed: CSRF cookie not set
So after working on this project for a while this is what I learned regarding the CSRF using Django.
First of all, if you are using django templates, or in any cases where your back-end and front-end are running behind the same server the most common practice is to use session for authentication.
This is activated by default by DRF.
This means that in your DRF configuration if you do not explicitly set the DEFAULT_AUTHENTICATION_CLASSES option default authentication will be set to Session + BasicAuth.
In this configuration you'll need to manage the CSRF token as described in the documentation (https://docs.djangoproject.com/en/4.0/ref/csrf/).
If your back-end and front-end are separated as in my case, using CSRF is not the only solution or even the recommended one.
As in my case I use JWT behind IAP (Identity Aware Proxy, provided by google). I had to write my own authentication classes and then use it in my DRF conf:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'main.authentication_backend.custom_auth.IAPAuthentication'],
...
}
Here is explain how to write your own authentication class: https://www.django-rest-framework.org/api-guide/authentication/#custom-authentication.

Multiple Token Authentication in Django Rest Framework

How can a user login in multiple devices because what we have is just a single Token Authentication on our django app. As an authenticated user when I login on Google Chrome it works fine but when I visit at the mozilla time and I logged out at the chrome the token that has been created has been deleted upon logout so when I login at mozilla, the token is already gone and we can not log-in on mozilla and throws a Forbidden response on the console.
You're question is a little convoluted, but I think you are getting at the problem referenced here:
https://github.com/tomchristie/django-rest-framework/issues/601
The official token authentication does not support (and unfortunately has no intention of supporting) multiple tokens, but you may be able to use django-rest-knox, available here: https://github.com/James1345/django-rest-knox
Edit: I previously recommended django-rest-multitoken, but django-rest-knox seems to be more actively maintained.
It's been years since this question was asked, I worked around this issue with few lines of code, I hope someone will benefit from this.
DRF authentication relies on two things:
Token model (which is the source of this issue).
Authentication class.
I provided my own implementations of these two, and passed them to DRF.
# models.py
from django.conf import settings
from django.db import models
from rest_framework.authtoken.models import Token
class MultiToken(Token):
user = models.ForeignKey( # changed from OneToOne to ForeignKey
settings.AUTH_USER_MODEL, related_name='tokens',
on_delete=models.CASCADE, verbose_name=_("User")
)
Then I implemented an Authentication class, just to override the model.
# appname.authentication.py
from rest_framework.authentication import TokenAuthentication
from appname.models import MultiToken
class MultiTokenAuthentication(TokenAuthentication):
model = MultiToken
Pass this Authentication class, to DRF.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'appname.authentication.MultiTokenAuthentication',
],
...
}
of course, Since I inherited from DRF Token model, I had to remove rest_framework.authtoken from INSTALLED_APPS.
I also, changed the ObtainAuthToken APIView to suit this new change.
class LoginApi(ObtainAuthToken):
def post(self, request, *args, **kwargs):
context = dict(request=request, view=self)
serializer = self.serializer_class(data=request.data, context=context)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
update_last_login(None, user, )
token = MultiToken.objects.create(user=user)
data = {'token': token.key}
return Response(data)
HOPE THIS HELPS.

Django Rest Framework remove csrf

I know that there are answers regarding Django Rest Framework, but I couldn't find a solution to my problem.
I have an application which has authentication and some functionality.
I added a new app to it, which uses Django Rest Framework. I want to use the library only in this app. Also I want to make POST request, and I always receive this response:
{
"detail": "CSRF Failed: CSRF token missing or incorrect."
}
I have the following code:
# urls.py
from django.conf.urls import patterns, url
urlpatterns = patterns(
'api.views',
url(r'^object/$', views.Object.as_view()),
)
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.views.decorators.csrf import csrf_exempt
class Object(APIView):
#csrf_exempt
def post(self, request, format=None):
return Response({'received data': request.data})
I want add the API without affecting the current application.
So my questions is how can I disable CSRF only for this app ?
Note: Disabling CSRF is unsafe from security point of view. Please use your judgement to use the below method.
Why this error is happening?
This is happening because of the default SessionAuthentication scheme used by DRF. DRF's SessionAuthentication uses Django's session framework for authentication which requires CSRF to be checked.
When you don't define any authentication_classes in your view/viewset, DRF uses this authentication classes as the default.
'DEFAULT_AUTHENTICATION_CLASSES'= (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),
Since DRF needs to support both session and non-session based authentication to the same views, it enforces CSRF check for only authenticated users. This means that only authenticated requests require CSRF tokens and anonymous requests may be sent without CSRF tokens.
If you're using an AJAX style API with SessionAuthentication, you'll need to include a valid CSRF token for any "unsafe" HTTP method calls, such as PUT, PATCH, POST or DELETE requests.
What to do then?
Now to disable csrf check, you can create a custom authentication class CsrfExemptSessionAuthentication which extends from the default SessionAuthentication class. In this authentication class, we will override the enforce_csrf() check which was happening inside the actual SessionAuthentication.
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
class CsrfExemptSessionAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
return # To not perform the csrf check previously happening
In your view, then you can define the authentication_classes to be:
authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)
This should handle the csrf error.
Easier solution:
In views.py, use django-braces' CsrfExemptMixin and authentication_classes:
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.views.decorators.csrf import csrf_exempt
from braces.views import CsrfExemptMixin
class Object(CsrfExemptMixin, APIView):
authentication_classes = []
def post(self, request, format=None):
return Response({'received data': request.data})
Modify urls.py
If you manage your routes in urls.py, you can wrap your desired routes with csrf_exempt() to exclude them from the CSRF verification middleware.
import views
from django.conf.urls import patterns, url
from django.views.decorators.csrf import csrf_exempt
urlpatterns = patterns('',
url(r'^object/$', csrf_exempt(views.ObjectView.as_view())),
...
)
Alternatively, as a Decorator
Some may find the use of the #csrf_exempt decorator more suitable for their needs
for instance,
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
#csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
should get the Job Done!
For all who did not find a helpful answer. Yes DRF automatically removes CSRF protection if you do not use SessionAuthentication AUTHENTICATION CLASS, for example, many developers use only JWT:
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
But issue CSRF not set may be occurred from some another reason, for exmple you not correctly added path to you view:
url(r'^api/signup/', CreateUserView), # <= error! DRF cant remove CSRF because it is not as_view that does it!
instead of
url(r'^api/signup/', CreateUserView.as_view()),
I tried a few of the answers above and felt creating a separate class was a little overboard.
For reference, I ran into this problem when trying to update a function based view method to a class based view method for user registration.
When using class-based-views (CBVs) and Django Rest Framework (DRF), Inherit from the ApiView class and set permission_classes and authentication_classes to an empty tuple. Find an example below.
class UserRegistrationView(APIView):
permission_classes = ()
authentication_classes = ()
def post(self, request, *args, **kwargs):
# rest of your code here
If you do not want to use session based authentication, you can remove Session Authentication from REST_AUTHENTICATION_CLASSES and that would automatically remove all csrf based issues. But in that case Browseable apis might not work.
Besides this error should not come even with session authentication. You should use custom authentication like TokenAuthentication for your apis and make sure to send Accept:application/json and Content-Type:application/json(provided you are using json) in your requests along with authentication token.
You need to add this to prevent default session authentication: (settings.py)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
Then: (views.py)
from rest_framework.permissions import AllowAny
class Abc(APIView):
permission_classes = (AllowAny,)
def ...():
You need to be absolutely sure, that you want to switch off CSRF protection.
Create file authentication.py and place it wherever you want in your project. For example, in folder session_utils.
Place this code in the file:
from rest_framework.authentication import SessionAuthentication
class SessionCsrfExemptAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
pass
When you want to make POST, PUT, PATCH or DELETE requests to your view be sure that you've changed SessionAuthentication to SessionCsrfExemptAuthentication from the new file. View example:
#api_view(["POST"])
#authentication_classes([SessionCsrfExemptAuthentication])
#permission_classes([IsAuthenticated])
def some_view(request) -> "Response":
# some logic here
return Response({})
This trick allow you to override method (pass) enforce_csrf and the new session authentication class will skip CSRF check.
✌️
I am struck with the same problem. I followed this reference and it worked.
Solution is to create a middleware
Add disable.py file in one of your apps (in my case it is 'myapp')
class DisableCSRF(object):
def process_request(self, request):
setattr(request, '_dont_enforce_csrf_checks', True)
And add the middileware to the MIDDLEWARE_CLASSES
MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)
My Solution is shown blow. Just decorate my class.
from django.views.decorators.csrf import csrf_exempt
#method_decorator(csrf_exempt, name='dispatch')
#method_decorator(basic_auth_required(
target_test=lambda request: not request.user.is_authenticated
), name='dispatch')
class GenPedigreeView(View):
pass
When using REST API POSTs, absence of X-CSRFToken request header may cause that error.
Django docs provide a sample code on getting and setting the CSRF token value from JS.
As pointed in answers above, CSRF check happens when the SessionAuthentication is used. Another approach is to use TokenAuthentication, but keep in mind that it should be placed first in the list of DEFAULT_AUTHENTICATION_CLASSES of REST_FRAMEWORK setting.
If you are using an exclusive virtual environment for your application, you can use the following approach without effective any other applications.
What you observed happens because rest_framework/authentication.py has this code in the authenticate method of SessionAuthentication class:
self.enforce_csrf(request)
You can modify the Request class to have a property called csrf_exempt and initialize it inside your respective View class to True if you do not want CSRF checks. For example:
Next, modify the above code as follows:
if not request.csrf_exempt:
self.enforce_csrf(request)
There are some related changes you'd have to do it in the Request class
This could also be a problem during a DNS Rebinding attack.
In between DNS changes, this can also be a factor. Waiting till DNS is fully flushed will resolve this if it was working before DNS problems/changes.
For me, using django 3.1.5 and django rest framework 3.12 the solution was way easier.
It happened to me that on a views.py file I had defined this two methods:
#api_view(['POST'])
#permission_classes((IsAuthenticated, ))
def create_transaction(request):
return Response(status=status.HTTP_200_OK)
def create_transaction(initial_data):
pass
On my urls.py:
urlpatterns = [
path('transaction', views.create_transaction, name='transaction'),
]
Django was picking the latest and throwing the error. Renaming one of the two solved the issue.
Code bellow would remove demand for CSRF. Even anon user would be able to send request.
from typing import List, Any
class Object(APIView):
authentication_classes: List = []
permission_classes: List[Any] = [AllowAny]
...
...
Removing CSRF check is not always the only (or best) solution. Actually, it's an important security mechanism for SessionAuthentication.
I was having the same issue when trying to authenticate with JWT and doing a POST request.
My initial setup looked like this:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework.authentication.SessionAuthentication",
"django_cognito_jwt.JSONWebTokenAuthentication",
),
...
}
As SessionAuthentication was checked first in the list, the CSRF error was raised. My solution was as simple as changing the order to always check JWT auth first. Like this:
"DEFAULT_AUTHENTICATION_CLASSES": (
"django_cognito_jwt.JSONWebTokenAuthentication",
"rest_framework.authentication.SessionAuthentication",
),
At the end, SessionAuthentication for me is only used in the django admin panel and 99% of the requests goes to the API that uses JWT auth.

django - get 'REMOTE_USER' from middleware class

We use kerberos for our authentication. Using
request.META['REMOTE_USER']
I am able to get the authenticated user in my views.py however when I move this to a custom middleware class inside the
def process_request(self, request):
I keep getting the following error
user = request.META['REMOTE_USER']
KeyError: 'REMOTE_USER'
How do I read the authenticated user from my middleware?
I think the problem is your middleware is executing first than the middleware that fill out that info. Try to change the order of your middleware in the settings.py file.