0

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

adam
  • 655
  • 1
  • 10
  • 31

3 Answers3

2

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.

akdev
  • 586
  • 2
  • 6
  • 18
1

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.

lopalcar
  • 33
  • 1
  • 9
0

you can add csrf exception for request.

or follow:- https://flask-jwt-extended.readthedocs.io/en/3.0.0_release/tokens_in_cookies/

Ajay K
  • 183
  • 11