Question

I am using normal flask web + flask-restful. So I need CSRF protection for web but not for REST.

The moment I enable CsrfProtect(app) of flask-wtf, all my post unit tests for flask-restful return a 400.

Is there a way to disable CSRF protection for REST services since they are coming from mobile handsets without session handling anyway, hence CSRF wouldn't make much sense.

This is how I test it:

rv = self.client.post('api/v1.0/verify-email', environ_base={'REMOTE_ADDR': '127.0.0.1'}, headers={'Content-Type':'application/json'}, data=json.dumps(data))
self.check_content_type(rv.headers)
eq_(rv.status_code, 412)
Was it helpful?

Solution

You can use the @csrf.exempt decorator, which you need to add directly on the API object, with the decorators argument; this would apply the decorator to all API routes:

csrf_protect = CsrfProtect(app)
api = restful.Api(app, decorators=[csrf_protect.exempt])

You cannot use resource method decorators as they are not the final view functions that the exempt decorator requires to work.

It appears you cannot protect individual resources and exempt others; this is a limitation in the method used by Flask-Wtf's method of recording what views are exempted.

OTHER TIPS

More simple solution (related commit):

csrf.exempt(api_blueprint)

And here is a complete demo:

from flask import Flask, Blueprint
from flask_wtf import CSRFProtect

app = Flask(__name__)
csrf = CSRFprotect(app)

api_blueprint = Blueprint('api', __name__)
csrf.exempt(api_blueprint)

app.register_blueprint(api_blueprint)

I had the exact same issue and I kept getting a generic 401 Unauthorized error, while my postman calls were working absolutely fine. It took me a while to figure out that this was because I had enabled CSRF for my web-based forms. But my backend calls were served by Restful endpoints and CSRF Protection doesn't work with restful calls.

Here's how I fixed it:

In my settings file,

I set JWT_COOKIE_CSRF_PROTECT to False and that fixed my restful post calls.

Note:

Since you are setting this field in the global settings file, this action will disable CSRF Protection for all the API calls.

You can also disbale CSRF protection by setting the WTF_CSRF_ENABLED to False. This comes handy in your unit tests. For example, when you create a test app that you use in your unit tests and disable WTF_CSRF_ENABLED the tests shouldn't throw a CSRF token missing error:

app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping({'WTF_CSRF_ENABLED':False})
app.test_client().get("/api/v1/")

A workaround to CSRF protect a single API endpoint on a flask-restful blueprint could be done by checking the path with an @app.before_request decorator and activate protection on demand.

@app.before_request
def csrf_protect():
    if request.path.startswith("/api/v1/resource"):
        csrf.protect()

A precondition is that you have set the setting

WTF_CSRF_CHECK_DEFAULT = False

You could set WTF_CSRF_ENABLED = False in your test_config. If you only want to disable it for your tests that is.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top