My POST requests to flask backend only work with JWT_COOKIE_CSRF_PROTECT = False, but GET requests work
config:
CSRF_ENABLED = True
CORS_SUPPORTS_CREDENTIALS = True
JWT_TOKEN_LOCATION = ['cookies']
I access flask through axios from the Vue app
const path1 = `/limit_engine/balance`;
axios
.post(path1, { withCredentials: true })
.then((response) => {
console.log(response.data["balance"]);
})
.catch((error) => {
console.error(error);
});
https://flask-jwt-extended.readthedocs.io/en/stable/options/#jwt-cookie-csrf-protect
suggests JWT_COOKIE_CSRF_PROTECT should be always True in production, so I cannot keep it False then
Try to debug the request by examining headers. If you are sending requests from the browser, you can use any of Dev Tools (Chrome for example). Take a look at the Network tab, look for your POST request, find out which cookies are sent.
If you can't find CSRF token in the request then you should pass it from the backend to the frontend and keep it in cookies storage.
After whole morning having trouble with this I realized CSRF token is only read from request headers as seen here: https://flask-jwt-extended.readthedocs.io/en/stable/_modules/flask_jwt_extended/view_decorators/ not from cookies, so in Vue you need to manually append this header to your requests.
Relevant source code to add to your flask app and to your Vue app:
In flask app:
app.config['JWT_ACCESS_CSRF_HEADER_NAME'] = "X-CSRF-TOKEN"
app.config['JWT_REFRESH_CSRF_HEADER_NAME'] = "X-CSRF-REFRESH-TOKEN"
app.config['JWT_CSRF_IN_COOKIES'] = False
In your flask app login function:
from flask_jwt_extended import (
jwt_required, create_access_token,
jwt_refresh_token_required, create_refresh_token,
get_jwt_identity, set_access_cookies,
set_refresh_cookies, get_raw_jwt, get_csrf_token
)
new_token = create_access_token(identity=current_user.id, fresh=False)
new_refresh_token=create_refresh_token(identity=current_user.id)
response = jsonify({
'data': {
'message':'Ok',
'type': 'user',
'id': current_user.id,
'meta': {
'accessToken': new_token,
'access_csrf_token': get_csrf_token(new_token),
'refreshToken': new_refresh_token,
'refresh_csrf_token': get_csrf_token(new_refresh_token)
}
}
})
set_refresh_cookies(response, new_refresh_token)
set_access_cookies(response, new_token)
return (response)
In your Vue app in your login fuction "edit according if you use or not refresh token logic":
axios.defaults.headers.common['X-CSRF-TOKEN']=response.data.data.meta.access_csrf_token
axios.defaults.headers.common['X-CSRF-REFRESH-TOKEN']=response.data.data.meta.refresh_csrf_token
And lastly do the same in yout Vue TokenRefreshPlugin or the method you use
I guess there are more approaches like getting the CSRF headers from the cookies, but this one seems to work for me for now at least. The important point is adding this headers manually in Vue requests, because using axios.defaults.withCredentials = true is not enough.
Also check header includes csrf token in the requests as akdev suggests.
you can add csrf exception for request.
or follow:-
https://flask-jwt-extended.readthedocs.io/en/3.0.0_release/tokens_in_cookies/
Related
I'm building a web application with Django as my backend framework and DjangoRestFramework as webAPI, React Native as the frontend, and apisauce as an HTTP client. When I try to make a POST request from the frontend to the backend, I get this error :
CSRF Failed: CSRF token missing
I don't get the error when I use postman.
Is it good if I comment this out django.middleware.csrf.CsrfViewMiddleware ?
CSRF tokens are used to prevent cross-site request forgery attacks. This error implies that you are not providing CSRF in your POST request. I was wondering about your reason to use apisauce instead of axios, since axios automatic inclusion of the CSRF token.
Meaning you would not need to comment "django.middleware.csrf.CsrfViewMiddleware"
and ensure that your app is CSRF attack free.
EDIT:
Ok, if you must use apisauce, you can manual set the csrf token using following code:
import apisauce from 'apisauce';
const api = apisauce.create({
baseURL: 'https://yourbackend.com/api',
});
const csrftoken = getCookie('csrftoken');
api.setHeader('X-CSRFToken', csrftoken);
api.post('/endpoint', {
data: 'yourdata',
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
function getCookie(name) {
// you may have to change this function a bit
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
I'm using Django GraphQL JWT Library and Django GraphQL Auth
I keep getting this error
google chrome error
With this react code (trimmed for relevancy) on both http://localhost:3000/ and https://localhost:3000/
const [login] = useMutation(LOGIN_MUTATION, {
variables: {
email: email,
password: password
},
onCompleted: ({ tokenAuth }) => {
if (tokenAuth.success) {
setToken(tokenAuth.token);
}
}
});
Now when I run this mutation from the graphiql page it works and I end up with a JWT cookie but not on the react site
mutation {
tokenAuth(
email:"********"
password:"*********"
){
token
refreshToken
success
errors
}
}
This doesn't work
GRAPHQL_JWT = {
"JWT_COOKIE_SAMESITE": 'None',
"JWT_ALLOW_ARGUMENT": True
}
Adding these didn't work
"CSRF_COOKIE_SECURE": True,
"SESSION_COOKIE_SECURE": True,
"CSRF_COOKIE_SAMESITE": 'None',
"SESSION_COOKIE_SAMESITE": 'None',
"JWT_VERIFY_EXPIRATION": True,
Adding these to django settings also didn't work
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_SAMESITE = 'None'
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'None'
I've been stuck on this for about 3 days now and am about ready to throw myself in a river and go build tables. Please help.
Support for the JWT_COOKIE_SAMESITE setting was added for django-graphql-jwt on version v0.3.2. You can check the releases here releases. While the current django-graphql-auth package relies on django-graphql-jwt v0.3.0. Updating your requirements will solve this issue.
so the issue is you are using
"JWT_COOKIE_SAMESITE": 'None'
which only works if
"JWT_COOKIE_SECURE": True
and JWT_COOKIE_SECURE means the cookie will only be sent over HTTPS connection and this won't work with HTTP connection.
considering you have HTTP and the backend is using the same domain as frontend then all you need to add is
"JWT_COOKIE_SAMESITE": 'Lax'
"JWT_COOKIE_SECURE": False
cookies are default into “SameSite=Lax” which means cookies are only set when the domain in the URL of the browser matches the domain of the cookie
I'm building a separated VueJS/Django app where Django will communicate with the Vue frontend using JSON. In order to be able to use the standard session authentication and django-allauth i will deploy the two apps on the same server and on the same port.
Here is my problem: after i log in from the Vue app using Axios, i don't receive any response but i notice that a session is created on the db, so i'm assuming that i'm getting logged in. But if i try to reach and endpoint that prints request.user.is_authenticatedi get False, and request.user returns Anonymous, so i'm not logged in anymore. How can i solve this?
Here is my Axios code:
bodyFormData.append('login', 'root');
bodyFormData.append('password', 'test');
axios({
method: "post",
url: "http://127.0.0.1:8000/accounts/login/",
data: bodyFormData,
withCredentials: true,
headers: { "Content-Type": "application/json" },
})
.then(function (response) {
//handle success
console.log(response);
})
.catch(function (response) {
//handle error
console.log(response);
});
I think Django-Allauth supports AJAX authentication on its urls, but i don't understand how to make it return something and how can my Vue app stay authenticated once i submit the Axios form. Any advice is welcome!
I'm trying to send a POST request from a Vue.js template to my API created with Django.
When sending I get a 403 CSRF token missing or incorrect error. Since I separated the front and the back, I don't have a view with {csrf_token} on the Django side.
How do I send my form?
I tried some exemples on the web using cookies but i'm beginners and need more explaination about the POST subject and CSRF
I have a Djano View (and urls associated) juste like this :
def get_csrf_token(request):
token = get_token(request)
return JsonResponse({'token': token})
Whe i'm requesting the url, obtained the JSON with the token.
And on the Front side i'm using this method to get the Token :
getToken: function() {
this.loading = true;
this.$http.get('/qualite/get-token/')
.then((response) => {
this.token =response.data;
this.loading = false;
})
.catch((err) => {
this.loading = false;
console.log(err);
})
},
addNc: function() {
let headers = {
'Content-Type': 'application/json;charset=utf-8'
};
if(this.token !== '') {
headers['HTTP_X-XSRF-TOKEN'] = this.token
}
this.loading = true;
this.$http.post('/qualite/api/nc/',this.newNc, {headers: headers})
.then((response) => {
this.loading = false;
})
.catch((err) => {
this.loading = false;
console.log(err)
})
},
For the CSRF you get by default after user login aside with the session, if you're using SessionAuthentication (It's the default authentication used in DRF).
You have to send it with each request in the header, you can refer the this link to know more about the header sent, as it's name is changed and can be configured.
Note also that in the settings you have to make sure that CSRF_COOKIE_HTTPONLY is set to False (which is the default), to be able to read it from the client side JS.
Another path would be removing CSRF enforcement per requests (But it's highly not recommended for security concerns), you can find more about this in the answer here.
Use a Token-based authentification.
Same issue i was encountered with,
the problem was, i had used Class based view and at the time of registered the url i forget to mention as_view() with class Name.
ex:- class PostData(APIView)
before :- path('post_data', PostData)
after correction:- path('post_data', PostData.as_view())
I am trying to implement the following workflow.
Populate an html form
Submit it
The endpoint that is receiving the request(expressjs) does some processing and sends a request with the req.body to another backend(django)
Django returns a response to expressjs
The problem is that I am being stuck at the csrf level and more specifically getting this error:
invalid csrf token
403
ForbiddenError: invalid csrf token
Here is the code that I am using for the request:
router.post('/registration', function (req, res, next) {
axios.post('http://localhost:8000/register_extended/', JSON.stringify(req.body), {
'Content-Type': 'application/json'
}).then(() => {
console.log('success')
}).catch(() => {
console.log('failure')
})
res.send('respond with a resource');
});
});
and here are my middlewares
app.use(bodyParser.urlencoded({ extended: true }));
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(csrfMiddleware);
How can I provide a valid csrf token for my needs? Axios is supposed to the handling itself, thats why I chose it.
Django uses CSRF tokens to prevent CSRF attacks from untrusted domains. You can whitelist your site or IP where express is running to make post requests without a CSRF token. Add this to your Django 'settings.py':
CSRF_TRUSTED_ORIGINS = [
"yoursite.runningexpress.here.com"
]