I spent a several hours debugging a validation issue with Flask-WTF. The issue like many others was a CSRF validation issue. However, mine was not caused by any of the common issues I have found.
The standard Flask-WTF implementation of CSRF requires two things be delivered to the browser.
One: The hidden CSRF form field e.g.
<input id="csrf_token" name="csrf_token" type="hidden" value="ImYzODdmZTdhYTRlMmNkYWRjYmRlYWFmZjQxMDllZTQ1OWZmYzg3MTki.XKvOPg.gUCkF9j-vg0PrL2PRH-v43GeHu0">
Two: The session cookie HTTP response header e.g.
Set-Cookie: session=eyJjc3JmX3Rva2VuIjoiZjM4N2ZlN2FhNGUyY2RhZGNiZGVhYWZmNDEwOWVlNDU5ZmZjODcxOSJ9.XKvOPg.a3-W62MHvaGVkv2GYCi-dgpLE3Y; HttpOnly; Path=/
If either of these are missing the browser will fail to send the proper CSRF validation. Of course, this in turn causes the form validation to fail.
If the csrf_token hidden field is present in the form but the session cookie is missing, you will receive the following response when the form is submitted...
Bad Request
The CSRF session token is missing.
In my case the session cookie was missing because of a bug in my code. I needed to serve a custom HTTP header across the entire Flask site. I included it like this...
class LocalFlask(Flask):
def process_response(self, response):
response.headers['my-header'] = 'My Header Value'
return response
app = LocalFlask(__name__)
This however causes anything that rellys on the the Flask.response.headers method to fail. One of those is Flaks-WTF setting the session cookie HTTP header.
This can be solved by adding the super() method to the LocalFlask class so that it inherits methods form the Flask class.
class LocalFlask(Flask):
def process_response(self, response):
response.headers['my-header'] = 'My Header Value'
#LocalFlask inherits methods from Flask
super(LocalFlask, self).process_response(response)
return response
app = LocalFlask(__name__)