41

I have previous experience in Django. If add line {csrf_token} in Django templates then Django handles the functionalities of csrf_token. But when I am trying to develop an API using Django REST Framework then I get stuck. How can I add and handle functionalities like csrf_token in API (back end, developed using Django REST Framework) and React Native/React JS (front end) like Django templates?

Taohidul Islam
  • 5,246
  • 3
  • 26
  • 39

2 Answers2

70

The first step is to get CSRF token which can be retrieved from the Django csrftoken cookie.

Now from the Django docs you can find out how to get the csrf token from the cookie by using this simple JavaScript function:

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]);
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

Now, you can retrieve the CSRF token by calling the getCookie('csrftoken') function

var csrftoken = getCookie('csrftoken');

Next you can use this csrf token when sending a request with fetch() by assigning the retrieved token to the X-CSRFToken header.

  fetch(url, {
    credentials: 'include',
    method: 'POST',
    mode: 'same-origin',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-CSRFToken': csrftoken
    },
    body: {}
   })
  }

Rendering the CSRF Token in React Forms:

If you are using React to render forms instead of Django templates you also need to render the csrf token because the Django tag { % csrf_token % } is not available at the client side so you need to create a higher order component that retrieves the token using the getCookie() function and render it in any form.

Lets add some line in csrftoken.js file.

import React from 'react';

var csrftoken = getCookie('csrftoken');

const CSRFToken = () => {
    return (
        <input type="hidden" name="csrfmiddlewaretoken" value={csrftoken} />
    );
};
export default CSRFToken;

Then you can simply import it and call it inside your form

import React, { Component , PropTypes} from 'react';

import CSRFToken from './csrftoken';


class aForm extends Component {
    render() {

        return (
                 <form action="/endpoint" method="post">
                        <CSRFToken />
                        <button type="submit">Send</button>
                 </form>
        );
    }
}

export default aForm;

The Django CSRF Cookie

React renders components dynamically that's why Django might not be able to set a CSRF token cookie if you are rendering your form with React. This how Django docs says about that:

If your view is not rendering a template containing the csrftoken template tag, Django might not set the CSRF token cookie. This is common in cases where forms are dynamically added to the page. To address this case, Django provides a view decorator which forces setting of the cookie: ensurecsrf_cookie().

To solve this issue Django provides the ensurecsrfcookie decorator that you need to add to your view function. For example:

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def myview(request):
ggorlen
  • 44,755
  • 7
  • 76
  • 106
orvi
  • 3,142
  • 1
  • 23
  • 36
  • 8
    I have used this: var cookie = cookies[i].toString().replace(/^([\s]*)|([\s]*)$/g, ""); instead of var cookie = jQuery.trim(cookies[i]); this way you don't need jQuery – Rik Schoonbeek Oct 30 '18 at 16:22
  • `cookies[i].trim()` is better, whould be nice if you can edit your post :) – rednaks Dec 27 '18 at 10:08
  • i am using react js as frontend and django just for backend apis. the html of react js is rendered by react js itself, django just provides the json data on calling its api. I saw the cookies on reactjs page after calling django apis, no cookies is set and why should it be set because django is just giving json data not setting cookies. so how can i get the csrf cookie value in such case ? – Yusuf Jun 12 '20 at 19:40
  • @Yusuf did u find a solution for this? I'm also using react to render the pages – Davi Wesley Aug 06 '20 at 12:11
  • 1
    @Davi - either use csrf by https://fractalideas.com/blog/making-react-and-django-play-well-together-single-page-app-model/ or switch to token based authentication with django knox which is better than default token auth by django rest and also closer to real world apis https://github.com/James1345/django-rest-knox – Yusuf Aug 07 '20 at 06:29
  • For token based you get a auth token on login save it and use it for any other apis by passing in header, once logout the token stays invalid – Yusuf Aug 07 '20 at 06:31
  • https://github.com/mir1198yusuf/feedapp_django_restapi_reactjs see my sample repo also see api docs in readme – Yusuf Aug 07 '20 at 06:32
  • @Yusuf, Does Knox authentication solve the issues of CSRF token from React? If we use Knox, do we still need to pass CSRF token ? – Neo Aug 30 '20 at 03:22
  • Yes it solves. We need to pass Knox auth token in header during api call. If you see my React code in above repo, I have added authorisation header while axios api call – Yusuf Aug 31 '20 at 14:36
  • We dont need to pass CSRF token anymore since our api now authenticates using auth token. So api becomes auth token based instead of CSRF. Other folks at SO can explain better if you ask a new question like what is diff between CSRF and auth token based – Yusuf Aug 31 '20 at 14:38
  • @Yusuf If you want to store token in cookies (like storing jwt refresh token in cookies), you still need to use csrf tokens to protect it. – famdude Jan 12 '23 at 07:05
  • @orvi It means that we have to create a view, which creates and sets a csrf token as cookie, and do that request before submitting every form? Then how does the django can figure out who has obtained that csrf token originally? Maybe a malicious user can do a request to that endpoint, capture a valid token, and add it to the form, to modify information of another user! – famdude Jan 12 '23 at 17:35
0

I used jquery for ajax in react, so in this case here is a solution :

let csrfcookie = function() {  // for django csrf protection
            let cookieValue = null,
                name = "csrftoken";
            if (document.cookie && document.cookie !== "") {
                let cookies = document.cookie.split(";");
                for (let i = 0; i < cookies.length; i++) {
                    let cookie = cookies[i].trim();
                    if (cookie.substring(0, name.length + 1) == (name + "=")) {
                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                        break;
                    }
                }
            }
            return cookieValue;
};

$.ajax({
       type: "POST",
       beforeSend: function(request, settings) {
                    if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                        request.setRequestHeader("X-CSRFToken", csrfcookie());
                    }
},

.... /// other codes
pyprism
  • 2,928
  • 10
  • 46
  • 85