Cómo recuperar / proporcionar un token CSRF a / desde Django como una API

Estoy trabajando en un proyecto que utiliza el marco REST de Django como backend (digamos en api.somecompany.com pero tiene una interfaz React.js (en www.somecompany.com ) que no atiende Django que realiza solicitudes AJAX.

No puedo, por lo tanto, usar el método tradicional de Django para que la plantilla incluya el token CSRF como este {% csrf_token %} {% csrf_token %}

Puedo realizar una solicitud a la api-auth\login\ Django REST api-auth\login\ url, que devolverá este encabezado: Set-Cookie:csrftoken=tjQfRZXWW4GtnWfe5fhTYor7uWnAYqhz; expires=Mon, 01-Aug-2016 16:32:10 GMT; Max-Age=31449600; Path=/ Set-Cookie:csrftoken=tjQfRZXWW4GtnWfe5fhTYor7uWnAYqhz; expires=Mon, 01-Aug-2016 16:32:10 GMT; Max-Age=31449600; Path=/ Set-Cookie:csrftoken=tjQfRZXWW4GtnWfe5fhTYor7uWnAYqhz; expires=Mon, 01-Aug-2016 16:32:10 GMT; Max-Age=31449600; Path=/ – pero no puedo recuperar esta cookie para enviarla de vuelta con mis solicitudes AJAX con X-CSRFToken (mi entendimiento es del subdominio separado), y no parece que se incluya automáticamente.

Aquí está mi código relevante:

 // 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; } 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)) { xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); } } }); 

A medida que se carga la página, lo llamo para asegurarse de que tengo un token:

 $.ajax(loginUrl, { method: "OPTIONS", async: false }) .done(function(data, textStatus, jqXHR) { console.log(jqXHR) app.csrftoken@ = $.cookie("csrftoken") console.log($.cookie("csrftoken")) console.log(app.csrftoken) }) .fail(function(jqXHR, textStatus, errorThrown) { console.log(jqXHR) }); 

Esto no es exactamente limpio, pero todavía no he probado el concepto.

¿Cuál es la forma ‘correcta’ de autenticar / proteger contra CSRF cuando el frontend y el backend están en diferentes puertos / dominios?

Puede, de hecho, hacer que esto funcione con la protección CSRF, utilizando una lista blanca de orígenes de solicitud permitidos.

Para hacer que esto funcione, tendrá que usar el Cross-Origin Resource Sharing (CORS). Para lograr esto, recomiendo crear una clase de middleware que configure sus credenciales de intercambio compartido. Hay una gran idea que describe cómo usar parte de la comstackción en encabezados de solicitud HTTP de origen cruzado. Si lo desea, puede insertar el token CSRF a través de middleware de esta manera, cambiando los valores de referencia de origen para la solicitud.

La aplicación django-cors-headers hace esto por ti. Puede ver cómo han negociado los tokens CSRF en su archivo de middleware , si está interesado.

Consulte los documentos de Django REST CORS para obtener más información sobre esto (recomiendan usar django-cors-headers).

Si todavía tienes dificultades, prueba:

  1. estableciendo el parámetro crossDomain de su solicitud AJAX en True . A veces, jQuery no procesará la solicitud si no se especifica.
  2. si aún no recibe un encabezado de token en la solicitud, intente ensure_csrf_cookie() decorador ensure_csrf_cookie() alrededor de su método de visualización. Ocasionalmente, cuando no hay una etiqueta de plantilla {% csrf_token %} en la página (si no está representando un formulario), Django no incluirá el token en la solicitud.