42

I'm trying to write a site in Django where the API URLs are the same as user-facing URLs. But I'm having trouble with pages which use POST requests and CSRF protection. For example, if I have a page /foo/add I want to be able to send POST requests to it in two ways:

  1. As an end user (authenticated using a session cookie) submitting a form. This requires CSRF protection.
  2. As an API client (authenticated using a HTTP request header). This will fail if CSRF protection is enabled.

I have found various ways of disabling CSRF, such as @csrf_exempt, but these all disable it for the entire view. Is there any way of enabling/disabling it at a more fine-grained level? Or am I just going to have to implement by own CSRF protection from scratch?

lucas
  • 1,910
  • 2
  • 21
  • 25

4 Answers4

65

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.

for instance,

from django.views.decorators.csrf import csrf_exempt
urlpatterns = patterns(
    # ...
    # Will exclude `/api/v1/test` from CSRF 
    url(r'^api/v1/test', csrf_exempt(TestApiHandler.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')
Community
  • 1
  • 1
Jossef Harush Kadouri
  • 32,361
  • 10
  • 130
  • 129
33

There is a section of Django's CSRF Protection documentation titled View needs protection for one path which describes a solution. The idea is to use @csrf_exempt on the whole view, but when the API client header is not present or invalid, then call a function annotated with @csrf_protect.

Daniel Trebbien
  • 38,421
  • 18
  • 121
  • 193
7

If you are you using class base view (CBV) and want to use the csrf_exempt decorator you will need to use the method decorator.

from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt

@method_decorator(csrf_exempt, name='dispatch')
class MyView(View):
    def post(self, request):
        pass  # my view code here
Thomas Turner
  • 2,722
  • 1
  • 27
  • 23
0

In my case, I am using JWT authentication plus csrf_token for some views. And for some reasons that I am unaware of, csrf_exempt does not work when I set it as a decorator or when I wrap the view name in the url patterns.

So here's what I ended up doing. I overrided the initialize_request available in the APIView class.

class ClasssName(views.APIView):
    def initialize_request(self, request, *args, **kwargs):
        setattr(request, 'csrf_processing_done', True) 
        return super().initialize_request(request, *args, **kwargs)
Lemayzeur
  • 8,297
  • 3
  • 23
  • 50