I have a Django REST backend (currently running as http://localhost:8000
) and a React front-end (on http://localhost:3000
) and I am struggling having the cross origin certification to work.
Most of the solutions I read here (see for example 1 or 2) were to do CORS_ORIGIN_ALLOW_ALL = True
and CORS_ALLOWED_ORIGINS = [*]
but it is not something I wish to do.
I want it to be safe for production and understand how to setup the csrf authentification properly.
Error message:
In Django console I see:
[24/Aug/2021 13:48:18] "OPTIONS /calc/ HTTP/1.1" 200 0
Forbidden (CSRF cookie not set.): /calc/
In the browser I have:
Failed to load resource: the server responded with a status of 403 (Forbidden)
Files:
Django REST API:
I have install django-cors-headers
.
- settings.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'maapi', # This is my app
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware', # Cors header are here
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
[...]
CORS_ORIGIN_ALLOW_ALL = False
CORS_ALLOWED_ORIGINS = [
"http://127.0.0.1:3000",
"http://localhost:3000",
]
CORS_ALLOW_CREDENTIALS = True
CSRF_TRUSTED_ORIGINS = [
"http://127.0.0.1:3000",
"http://localhost:3000",
]
- maapi/views.py:
class Calc(View):
"""
Run a static simulation.
"""
def post(self, request, *args, **kwargs):
rjson = json.loads(request.body)
try:
# do things
return JsonResponse(results)
except Exception as e:
return e, 500
- urls.py:
from maapi import views
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
path('calc/', views.Calc.as_view(), name='calc'),
# path('calc/', ensure_csrf_cookie(views.Calc.as_view()), name='calc'), # I tried this one too without more success
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
path('', include(router.urls)),
]
React Frontend:
I used the tutorial here.
- CrsfToken.js:
import React from 'react';
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// 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;
}
const csrftoken = getCookie('csrftoken');
const FormCsrfToken = () => {
return (
<input name="csrfmiddlewaretoken" value={csrftoken} type="hidden" />
);
};
export default FormCsrfToken;
export { csrftoken }
- App.js:
import { csrftoken } from './CsrfToken';
function App() {
[...]
const getdata = () => {
setFetching(true);
const bod = {
// Things
};
fetch("http://localhost:8000/calc/", {
method: "POST",
credentials: 'include',
mode: 'cors',
headers: {
'Accept': 'application/json',
"Content-Type": "application/json",
'X-CSRFToken': csrftoken,
},
body: JSON.stringify(bod),
})
.then((response) => {
return response.json();
})
.then((d) => {
// Do things
})
.catch((object) => {
setFetching(false);
openNotification();
});
};
Can you spot what is missing?