javascriptpythonflaskflask-jwt

Flask JWT Extended set cookie error


I have created an API that is supposed to answer to both mobile devices and web browsers. For example, /web/toys for web and /API/toys for JSON responses. I am using JW Tokens as a means of authentication.
I am displaying forms in HTML and in the background, I call jQuery Ajax's methods to POST to my APIs. I am keeping the access_tokens in the session cookie. To prevent CSRF attacks, I am using Flask-JWT-Extended.

When I decorate a view with @jwt_required and CSRF is set to True, I get missing JWT in headers and cookies, even when the cookies were being set and transferred. I checked the source code and found that it is important to set X-CSRF-TOKEN in the request header.
However, since the endpoint answers to both GET and POST calls, how can I set the headers in the GET call without resorting to loading the complete page using jQuery.?
Basically, I want to show the form on the webpage, and when the user clicks submit, the form be transferred using jQuery to the existing API.
If there is a better way to handle things, I would love to know it.
Thanks!


Solution

  • Author of Flask-JWT-Extended here. As you have discovered, with this extension we are currently doing CSRF protection for every type of request. However, CSRF protection is only really needed on state changing requests: See: https://security.stackexchange.com/questions/115794/should-i-use-csrf-protection-for-get-requests/115800

    The benefit of protecting all requests types is that if you have an endpoint that incorrectly changes state in a GET request (there is no technical reason this couldn't happen), it becomes vulnerable to CSRF attacks. Now if the backend is designed more 'up to spec', this is no longer a problem. It sounds like I need to update Flask-JWT-Extended to allow for ignoring CSRF protection on certain types of requests, just like how Flask-WTF operates. I'll try to get this updated today.

    Alternately, if your backend is serving JSON instead of html directly (such as a REST api backend and javascript frontend), you can use Ajax to do GET requests with CSRF tokens. In this use case, we could use an Ajax call along these lines.

       get (options) {
         let self = this
         $.ajax({
           method: 'GET',
           dataType: 'json',
           headers: {
             'X-CSRF-TOKEN': Cookies.get('csrf_access_token')
           },
           url: "some_url",
           success (result, statusText) {
             // Handle success
           },
           error (jqXHR, textStatus, errorThrown) {
             //handle error
           }
         })
       }
    

    EDIT: I also want to preserve the CSRF error messages if the CSRF token isn't present and you are using both headers and cookies for JWTs. Progress on both of these can be tracked here: