3

I have this simple Like button that works well when used with @csrf_exempt:

Template

<p id="like_count"> {{ topic.likes }}</p> 
<span data-type="topic" title="Like">       {% csrf_token %}
 <i class="fa fa-thumbs-up" id="liket" name="{{topic.id}}">  

Ajax

$(function(){
$('#liket').click(function(){
      $.ajax({
               type: "POST",
               url: "/like/",
               data: {
               'topic_id': $(this).attr('name'), 
               'csrfmiddlewaretoken': '{{csrf_token}}'
               },
               success: tlikeSuccess,
               dataType: 'html'               
                });
    });

});
function tlikeSuccess(data, textStatus, jqXHR)
{
    $('#like_count').html(data);
}

and views:

#@csrf_exempt 
def topic_like(request):

    args = {}
    if request.method == 'POST':

        user = request.POST.get('user')
        lu= request.user #User.objects.get(username= user)
        topic_id = int(request.POST.get('topic_id'))

        try:
            liked_topic = Topic.objects.get(id = topic_id)
        except:
            liked_topic = None  

        if TopicLike.objects.filter(liker=request.user.id, topic=topic_id).exists():

            liked_topic.likes -=1
            liked_topic.save()
            TopicLike.objects.filter(topic=topic_id, liker=request.user.id).delete()

        else:            
            liked_topic.likes +=1
            liked_topic.save()
            newliker = TopicLike(topic=topic_id, liker=request.user.id)
            newliker.save()          


    #args.update(csrf(request))
    args['likes'] = str(liked_topic.likes)
    return render(request, 'ajax_like.html', args)  

However I don't like this workaround that ignores the CSRF, as it could be vulnerable. On the other hand, I could not manage to return a new CSRF token to the template so I appreciate your hints to integrate CSRF into this button.

Jand
  • 2,527
  • 12
  • 36
  • 66
  • The [Django docs](https://docs.djangoproject.com/en/1.8/ref/csrf/#ajax) suggest providing the csrf token in a header instead of in the post data. – Alasdair Oct 05 '15 at 17:02
  • @Alasdair please give me a complete answer. I don't know how to apply the doc's suggestions. – Jand Oct 05 '15 at 17:04
  • I think the docs are pretty clear. What did you try? What do you think is missing? Which part do you not understand? – Alasdair Oct 05 '15 at 18:55

2 Answers2

4

Django in its docs has defined to actually set the header on AJAX request, while protecting the CSRF token from being sent to other domains using settings.crossDomain in jQuery 1.5.1 and newer.

Acquiring the token:

// using jQuery
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');

Setting the CSRFToken in the header:

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);
        }
    }
});

Also try this method to embed CSRF token in each AJAX request given on this SO link.

$(function () {
    $.ajaxSetup({
        headers: { "X-CSRFToken": getCookie("csrftoken") }
    });
});
Community
  • 1
  • 1
Rahul Gupta
  • 46,769
  • 10
  • 112
  • 126
  • I added these functions to my ajax file and in my views removed `@csrf_exempt ` but I get this error in the consul `Uncaught ReferenceError: csrftoken is not defined`. Any ideas? – Jand Oct 05 '15 at 17:33
  • Sorry, I'm confused. Do you mean I should add all the 3 functions above to my ajax.js without changing my original ajax function and views? – Jand Oct 05 '15 at 17:54
  • Just the first 2 methods, the last(3rd) function is an alternative to the 2nd function. – Rahul Gupta Oct 05 '15 at 17:58
  • When I add 2 functions above to my ajax.js, I get `403 (FORBIDDEN)` regardless of whether I update CSRF in the views or not. (Not I did not make any change to my original ajax function. Should I've had?) – Jand Oct 05 '15 at 18:07
  • These 2 functions are defined in the official docs on how to add a csrftoken to request. Please check the [docs](https://docs.djangoproject.com/en/1.8/ref/csrf/#ajax) if you have followed the exact procedure. – Rahul Gupta Oct 05 '15 at 18:10
0

you can add the csrf token manually, using javascript.

https://docs.djangoproject.com/en/1.8/ref/csrf/#ajax

eran
  • 6,731
  • 6
  • 35
  • 52