Search code examples
nginxcookieshttponlyflask-jwt-extended

Flask-JWT-Extended set cookies with double submit cookie method, prevent HTTP-only cookie


I'm using Flask-JWT-Extended and double submit cookie method from there for my Flask backend and React Frontend. So when user logs in from frontend, backend sets total of 4 different cookeis: csrf_access_token, csrf_refresh_token, access_token_cookie, refresh_token_cookie. Out of these 4 cookies, access_token_cookie and refresh_token_cookie should be HTTPonly cookie, and thus not accessible by JS and csrf_access_token and csrf_refresh_token are non-HTTPonly cookie. So the idea here is that HTTPOnly cookie holds user's session information with CSRF token and non-HTTPonly cookie holds the CSRF token and when POST request is made, CSRF token accessed by JS is sent to backend along with the other cookies.

This was working just fine in my development environment, two of the cookies were accessible by JavaScript and thus I could send csrf_acccess_token along with the request with withCredentials True, but when I deploy this to test environment with TLS using Nginx (Both backend and frontend), it is setting all 4 cookies as HTTPOnly cookie, and thus, I cannot make any POST request.

I'm not sure whether this was caused by the Nginx, but from what I can tell, I don't see much options to turn off 2 of the HTTPOnly cookies being registered from the backend.

Below is my configuration for flask-jwt-extended

CORS_HEADERS = "Content-Type,X-CSRF-TOKEN"
JWT_TOKEN_LOCATION = ["cookies"]
JWT_COOKIE_SECURE = True
#JWT_COOKIE_SAMESITE = None
JWT_ACCESS_TOKEN_EXPIRES = 600
JWT_REFRESH_TOKEN_EXPIRES = 1200
JWT_CSRF_IN_COOKIES = True
JWT_COOKIE_DOMAIN = ".mydomain.com"
#JWT_ACCESS_COOKIE_PATH = '/'
#JWT_REFRESH_COOKIE_PATH = '/'
JWT_COOKIE_CSRF_PROTECT = True
JWT_SECRET_KEY = "secret"

Any advice would be greatly appreciated!


Solution

  • Flask-JWT-Extended should never be setting the csrf cookies as httponly. I wonder if there is an nginx setting that is converting all cookies to httponly (something like proxy_cookie_path)?

    If that’s the case, another approach you could take it to set JWT_CSRF_IN_COOKIES to false, and use https://flask-jwt-extended.readthedocs.io/en/stable/api/#flask_jwt_extended.get_csrf_token to grab the csrf token when a JWT is created, return it as part of the JSON payload, and store it in localStorage instead of in those non-httponly cookies so that your JavaScript can still grab it when making requests.