React & Django - WARNING:django.request:Forbidden: /api/user - SessionAuthentication
I'ved created this Django & React project (https://github.com/axilaris/docker-django-react-celery-redis), and I'm having difficulties with accessing API with SessionAuthentication. You can easily test and spin off my code with:
- docker-compose build
- docker-compose up
I keep getting access issue with accessing /api/user (you can test with register, login)
backend_container | Forbidden: /api/user
backend_container | WARNING:django.request:Forbidden: /api/user
You can look more the logs here: https://gist.github.com/axilaris/7b7a5c50f4f7112b440eaf8ef8100d9d
In my django api code (backend/user_api/views.py):
class UserView(APIView):
permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
def get(self, request):
serializer = UserSerializer(request.user)
return Response({'user': serializer.data}, status=status.HTTP_200_OK)
In settings.py:
CORS_ALLOWED_ORIGINS = [
'http://localhost',
'http://127.0.0.1',
'http://0.0.0.0',
]
CORS_ALLOW_CREDENTIALS = True
INSTALLED_APPS = [
..
'corsheaders',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
...
]
and in react:
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
axios.defaults.withCredentials = true;
useEffect(() => {
client.get("/api/user")
.then(function(res) {
setCurrentUser(true);
})
.catch(function(error) {
setCurrentUser(false);
});
}, []);
why whenever after I login and I reload back localhost on the browser it has forbidden /api/user and goes back to the login page. I feel the CORS are setup correctly and credentials are set true on both backend and frontend.
You have:
Client (React) --> [HTTP Request with CSRF Token] --> Django Backend
/\
|
+--- [Session Authentication & CSRF Validation]
For SessionAuthentication to work, make sure Django's session cookie (sessionid) and CSRF token cookie (csrftoken) are correctly set and sent to the client. The HttpOnly flag should generally be set on the sessionid cookie to enhance security, as mentioned in this thread.
Since the session cookie (sessionid) should be HttpOnly, the React application will not directly interact with it. Instead, make sure the axios configuration correctly includes credentials with every request. That part seems to be configured correctly in your setup (axios.defaults.withCredentials set to true).
settings.py should include:
SESSION_COOKIE_HTTPONLY = True # Default value is True, which is recommended
SESSION_COOKIE_SAMESITE = 'Lax' # Consider 'None' if strictly necessary and secure is set
SESSION_COOKIE_SECURE = True # Set to True if you are using HTTPS
For CSRF cookie configuration:
CSRF_COOKIE_HTTPONLY = False # Should generally be False to allow JavaScript to read the value
CSRF_COOKIE_SECURE = True # Set to True if you are using HTTPS
Setting CSRF_COOKIE_HTTPONLY to False would make your application slightly more vulnerable to XSS attacks, as it allows JavaScript to access the CSRF cookie. A better approach would be to keep the CSRF token HttpOnly, and obtain it from a separate API endpoint designed to return the CSRF token explicitly, or include the CSRF token within the body of your page and have your JavaScript read it from there.
Although... as documented since 2016 in django/django PR 7700 and commit c27104a
Designating the CSRF cookie as
HttpOnlydoesn't offer any practical protection because CSRF is only to protect against cross-domain attacks. If an attacker can read the cookie via JavaScript, they're already on the same domain as far as the browser knows, so they can do anything they like anyway. (XSS is a much bigger hole than CSRF.)
The primary risk to consider when making CSRF tokens accessible to JavaScript is XSS -- Cross site scripting . If your application is vulnerable to XSS, an attacker could exploit this to bypass CSRF protection entirely. Therefore, any decision to expose CSRF tokens to JavaScript should be accompanied by rigorous XSS mitigation strategies. See, for instance, "Django XSS: Examples and Prevention" from StackHawk.
Test the flow of obtaining a CSRF token and session cookie upon login. Verify that subsequent requests from React include the CSRF token in the header and the session cookie automatically (even though the JavaScript cannot read the sessionid cookie due to the HttpOnly flag).
hello try this,
npm install http-proxy-middleware
Open the setupProxy.js file and add the following code:
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:8000',
changeOrigin: true,
})
);
};
other django settings seem ok to me, so dont touch them for now.
option 2
in package.json file, add this attribute at firsst indent level.
"proxy":"http://localhost:8000"
google about how cookies are set, you will understand the secure context concept, and why different origins are security issue when setting cookies. explanation not provide for brevity. also make sure, use /api/... and relative to it for further requests for the same reason.
personal opinion: django is a good man, react is magician here. the defaults of react are quite tricky to understand, and i missed deadlines, so i left react for sveltkit.
Adding a simple
CSRF_TRUSTED_ORIGINS = [
'http://localhost:3000/*',
'http://localhost:3000/*'
]
Could help!