He visto mucho sobre esto en SO, pero nada puede solucionar mi problema.
Problema:
Con el middleware CSRF habilitado, Django responde con 403 en la solicitud de formulario AJAX, indicando:
"Cookie CSRF no configurada".
Siguiendo la documentación , se implementó una funcionalidad JS que establece el encabezado "X-CSRFToken" personalizado .
Funciona como se esperaba, obtiene la cookie "csrftoken" del navegador y la publica junto con la solicitud AJAX:
x-csrftoken: 1a0u7GCQG0wepZHQNThIXeYpMy2lZOf2Pero la respuesta sigue siendo 403.
Soluciones probadas:
Probé todo lo que pude encontrar en SO o en la web, específicamente:
Comprobando que el middleware está habilitado:
MIDDLEWARE_CLASSES = [ ... 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', ... ]Diferentes navegadores con cookies habilitadas;
Decorando mi vista con @ensure_csrf_cookie ;
Configurando {% csrf_token %} en mi plantilla;
Usando el atajo de render que toma el contexto de solicitud correcto;
Configurar CSRF_COOKIE_NAME y CSRF_HEADER_NAME personalizados en mi settings.py ;
Establecer explícitamente CSRF_COOKIE_SECURE = False y CSRF_COOKIE_HTTPONLY = False ;
Establecer explícitamente la configuración CSRF_TRUSTED_ORIGINS ;
Pruebas en servidor de desarrollo y producción;
Incluso request.META["CSRF_COOKIE_USED"] = True en mi opinión, como sugirió alguien.
Y todavía no tengo nada.
Encabezados:
Si uso @csrf_exempt e imprimo print(request.META) en mi opinión, está claro que el encabezado personalizado "X-CSRFToken" está presente en la solicitud y está formateado de acuerdo con la documentación de Django, con el prefijo "HTTP_", guiones reemplazados con guiones bajos, todo en mayúsculas : "HTTP_X_CSRFTOKEN" .
Aún más, su valor coincide con la cookie establecida por Django.
Galletas:
Lo extraño es que, si trato de print(request.COOKIES) en mi vista, en la página y en la carga del formulario puedo ver la cookie "csrftoken" allí, pero el diccionario está vacío en la solicitud AJAX . ¿Puede ser el problema?
Desesperado por encontrar lo que realmente está mal. Gracias por leer esto.
Ok, el problema es bastante simple entonces:
Fetch API no envía credenciales de forma predeterminada. Según MDN :
La propiedad de solo lectura de credenciales de la interfaz de solicitud indica si el agente de usuario debe enviar cookies desde el otro dominio en el caso de solicitudes de origen cruzado. Esto es similar al indicador withCredentials de XHR, pero con tres valores disponibles.
El valor predeterminado es omit y nunca envía cookies. Solo necesita agregar same-origin a los argumentos de la función fetch() :
fetch(formUrl, { ... credentials: 'same-origin', ... })Y estarás listo para ir :)