2

I'm using using django rest framework browsable api with ModelViewSet to do CRUD actions and want to use permissions.IsAuthenticatedOrReadOnly, but when I'm logged and try to DELETE or PUT I get "detail": "CSRF Failed: CSRF token missing or incorrect."

My view looks like this

class objViewSet(viewsets.ModelViewSet):
    queryset = obj.objects.all()
    serializer_class = objSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

Settings.py

REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
    'rest_framework.permissions.AllowAny',
),

Serializer is just

class ObjSerializer(serializers.ModelSerializer):
    class Meta:
        model = Obj

Although when I delete permission_classes (so the default allowAny triggers) I can it works just fine.

What I want

To be able to PUT/DELETE only when I'm authenticated. I don't know how to send CSRF token, when all happens automatically (modalviewset does the whole work)

Jakub Królak
  • 63
  • 1
  • 8
  • You want to remove csrf checks globally? or seperate non csrf views ? – itzMEonTV Aug 31 '16 at 08:07
  • In case you won't get any better solution here is mine: I bet you are using 'rest_framework.authentication.SessionAuthentication' as one of the authentication classes. Remove it from settings & everything should work correctly. – Kamil Rykowski Aug 31 '16 at 12:54

4 Answers4

4

In your REST_FRAMEWORK settings you haven't mentioned the Authentication scheme so DRF uses the default authentication scheme which is SessionAuthentication. This scheme makes it mandatory for you to put csrf token with your requests. You can overcome this by:

  1. To make this setting for the whole project in settings.py add


    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
             'rest_framework.authentication.BasicAuthentication',
        )}

  1. To make this setting in specific view do the following in your view


    class objViewSet(viewsets.ModelViewSet):
        queryset = obj.objects.all()
        serializer_class = objSerializer
        permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
        authentication_classes = (BasicAuthentication,)

source: http://www.django-rest-framework.org/api-guide/authentication/#sessionauthentication

BTW, csrf token is saved as a cookie called 'csrftoken'. You can retrieve it from HTTP Response and attach it to your request header with the key 'X-CSRFToken'. You can see some details of this on: https://docs.djangoproject.com/en/dev/ref/csrf/#ajax

Sarosh Khatana
  • 501
  • 6
  • 16
0

You might have used SessionAuthentication and session auth checks for the csrf_token always and you can avoid the checks by exempting but Lets not do that.

I think you can keep both Authentication classes. or just TokenAuthentication. ie,

'DEFAULT_AUTHENTICATION_CLASSES': (
    # 'rest_framework.authentication.BasicAuthentication',
    'rest_framework.authentication.TokenAuthentication',
    'rest_framework.authentication.SessionAuthentication',

    # 'oauth2_provider.ext.rest_framework.OAuth2Authentication',
    # 'rest_framework_social_oauth2.authentication.SocialAuthentication',
),

And If you are not going for TokenAuth and just the session Auth. you can always pass csrf_token via X-CSRFToken header. Or you just can go for csrf_except things. which will avoid the csrf missing issues.

This should work. Also refer the links below.

ref: https://stackoverflow.com/a/26639895/3520792

ref: https://stackoverflow.com/a/30875830/3520792

Community
  • 1
  • 1
Vipul Vishnu av
  • 486
  • 1
  • 5
  • 15
  • "you can always pass csrf_token via X-CSRFToken header" that's exactly what I don't know how to do because I'm using ModelSetView – Jakub Królak Aug 31 '16 at 13:59
  • Its not about what view you use. You are using session and the server is not getting csrf_token from client side. You just pass csrf_token in the X-CSRFToken request header. There are ways to get the csrf_token in the client side. ref:- http://stackoverflow.com/questions/22063612/adding-csrftoken-to-ajax-request , http://stackoverflow.com/questions/28417781/jquery-add-csrf-token-to-all-post-requests-data Just investigate on it. Its just your request header should have it. – Vipul Vishnu av Aug 31 '16 at 14:18
  • I get how to do it from client side using ajax. But my client side (browsable api) is generated automatically by django, meaning - I don't have access to that. Is there any option to modify my views (so they include CSRFToken) inside django code or do I have to override this borwsable api view and then use ajax when clicked "PUT"/"DELETE"? – Jakub Królak Aug 31 '16 at 15:09
  • I have the same kind of work running in my local. I just checked the API put post from browsable API interface provided by Django REST framework 3.2.4. Its working for me. with the the settings given in the answer. if you are doing anything different just help me to reproduce it. – Vipul Vishnu av Sep 01 '16 at 05:48
0

You shoud add CSRF token to your request. If you do it with JSON request you should add this in your JS code. It adds CSRF token from user cookies.

function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
    var cookies = document.cookie.split(';');
    for (var i = 0; i < cookies.length; i++) {
        var cookie = jQuery.trim(cookies[i]);
        // Does this cookie string begin with the name we want?
        if (cookie.substring(0, name.length + 1) === (name + '=')) {
            cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
            break;
        }
    }
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});
Maks
  • 1,527
  • 1
  • 13
  • 16
-3

You can remove CSRF check on individual urls. Try this on your urls.py:

url(r'^/my_url_to_view/', csrf_exempt(views.my_view_function), name='my_name'),
navid
  • 566
  • 6
  • 15