18

I have an application in Django 1.9 that uses SessionMiddleware. I would like to create an API for this application inside the same project, but when doing a POST request it does not work the @csrf_exempt annotation.

I am doing the requests throw Postman and this is what I have so far:

settings.py

MIDDLEWARE_CLASSES = [ 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.cache.UpdateCacheMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'a9.utils.middleware.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'a9.core.access.middleware.AccessMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', ] OAUTH2_PROVIDER = { # this is the list of available scopes 'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'groups': 'Access to your groups'} } CORS_ORIGIN_ALLOW_ALL = True CORS_ALLOW_METHODS = ( 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', ) CORS_ALLOW_HEADERS = ( 'accept', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', ) REST_FRAMEWORK = { # Use Django's standard `django.contrib.auth` permissions, # or allow read-only access for unauthenticated users. 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly', #'rest_framework.permissions.IsAuthenticated', ], 'DEFAULT_AUTHENTICATION_CLASSES': ( 'oauth2_provider.ext.rest_framework.OAuth2Authentication', #'rest_framework.authentication.TokenAuthentication', ) } 

urls.py

urlpatterns = [ url(r'^v1/', include([ url(r'^', include(router.urls)), url(r'^auth/', MyAuthentication.as_view()), url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')), url(r'^admin/', include(admin.site.urls)), ])), ] 

views.py

@method_decorator(csrf_exempt, name='dispatch') class MyAuthentication(TemplateView): def post(self, request, *args, **kwargs): return HttpResponse('Hello, World!') 

After this I get always a CSRF verification failed error.

I asked this question in the IRC channel of django-rest-framework but I still have no answer. Please, any advice will be very appreciated.

3
  • Did you try importing View instead of TemplateView inside the class ? Commented Jan 18, 2017 at 5:18
  • There is no post method available for TemplateView. Commented Jan 18, 2017 at 5:36
  • Yes, that was the first attempt. Actually, TemplateView inherits from View, therefore it has a post method: docs.djangoproject.com/en/1.9/ref/class-based-views/base/… Commented Jan 18, 2017 at 17:19

4 Answers 4

10

DO NOT USE csrf_exempt with Django REST framework.

This won't work because the SessionAuthentication enforces the csrf check anyway.

Please make sure you use the csrf token in your AJAX requests. Django has a comprehensive documentation about it

Sign up to request clarification or add additional context in comments.

6 Comments

As I mentioned in the question, I need to exempt the csrf token for the API application in order to allow other clients to consume the endpoints. The problem is not about AJAX requests, is about how to make that the Session Middleware works for other applications, i.e. mobiles, without removing it from the existing project.
Then you get it wrong. Token authentication or OAuth should be used for non browser clients.
I don't think you actually understand the question. An API is different than doing AJAX request. AJAX requests are for consuming the endpoints, NOT to create an API
Actually I don't think you got my point. API will respond to various requests, some of which may be AJAX. My point is that AJAX requests should be authenticated with the SessionAuthentication while other machine requests (iOS, Android, curl, wget or others) should be authenticated with token. Note that only SessionAuth requires a CSRF.
Sorry but I did, that's how I know that what you wrote in these comments it returns to the same question, does not solve it at all: How to make the CSRF disabled only for an API app?, and about the answer I found it already and post it as an answer in this same page.
|
9

You need to decorate the csrf_exempt inside the dispatch method.

class MyView(FormView): @method_decorator(csrf_exempt) def dispatch(self, *args, **kwargs): return super(MyView, self).dispatch(*args, **kwargs) def post(self, request, *args, **kwargs): # .... return super(MyView, self).post(request, *args, **kwargs) 

2 Comments

That is what @method_decorator(csrf_exempt, name='dispatch') does
Especially on View class
8

I found out the way to solve this. You need to create a middleware that calls before any Session Middlewares and then check against your desired urls or app to exempt the CSRF token validation. So, the code would be like this:

settings.py

MIDDLEWARE_CLASSES = [ 'api.middleware.DisableCSRF', # custom middleware for API 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.cache.UpdateCacheMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'a9.utils.middleware.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'a9.core.access.middleware.AccessMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', ] 

urls.py

app_name = "api" urlpatterns = [ url(r'^v1/', include([ url(r'^', include(router.urls)), url(r'^auth/', MyAuthentication.as_view()), url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')), url(r'^admin/', include(admin.site.urls)), ])), ] 

csrf_disable.py

from django.core.urlresolvers import resolve # django2 class DisableCSRF(object): """Middleware for disabling CSRF in an specified app name. """ def process_request(self, request): """Preprocess the request. """ app_name = "api" if resolve(request.path_info).app_name == app_name: setattr(request, '_dont_enforce_csrf_checks', True) else: pass # check CSRF token validation 

This will only check CSRF token against a specific app or url without removing all the CSRF. Also, this is django-rest-framework independent :)

Comments

0

In my case in django3.2 while using DRF, and function based view. I had to explicitly set permission_classes to []

@api_view(["GET"]) @permission_classes([]) def ping(*args, **kwargs): """ Used to double check that the api is up an running. """ return Response({"msg": "pong"}, status=200) 

I believe a class based view might be similar:

class PingView(ListAPIView): permission_classes = [] pagination_class = None serializer_class = None def get(self): return Response({"msg": "pong"}, status=200) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.