Flask-Security CSRF token
Asked Answered
A

5

11

I have a flask app that serves as REST API backend. I would like to implement token based authentication for the backend but in order to do that I need to retrieve the user token. Flask-Security documentation clearly says that to retrieve the token one needs to perform an HTTP POST with the authentication details as JSON data to the authentication endpoint. Unfortunately I don't understand how to retrieve the CSRF token needed to perform such request.

If I use the login page/template provided with the extension the CSRF token is passed to the client in the hidden field in the form. The question is:

how do I retrieve the CSRF token without accessing and parsing the login page, like for example from an angularJS app using $http methods or a mobile app?

Obviously I could avoid using Flask-Security and implement the pieces myself but I'm relatively inexperienced with webapps and I feel I might be approaching this the wrong way.

Acrogen answered 26/8, 2013 at 3:19 Comment(3)
How did you do this in the end?Venerable
I ended up implementing the security layer myself. Sorry I don't have a better answer for you.Acrogen
Note that there is no secure way to store the tokens in angularjs, see e.g. here. The secure option is to let the server store the token in a http-only cookie + enable CSRF protection. If all you need to talk to is angularjs you can just as well just use the session (+ anti-CSRF) then.Tajuanatak
T
5

I had a similar use case and ended solving it by following this example from the Flask-WTF docs: https://flask-wtf.readthedocs.org/en/latest/csrf.html#ajax

So by CSRF Protecting the app via CsrfProtect(app), the csrf_token() becomes available in all templates. Then you can easily make it available via a script tag:

<script type="text/javascript">
    var csrftoken = "{{ csrf_token() }}"
</script>

Now add the token to your post data for the Flask-Security /login endpoint.

Trilbi answered 14/10, 2013 at 22:26 Comment(1)
Thanks for the answer but I ended up implementing my own user management solution. Also this only works if you serve the pages with the Flask app and you render the template with Jinja. In the case you use the Flask app only for the backend (REST API) and the frontend is served with other methods you would have to provide an API endpoint to retrieve the token and render it in the json response. I'm not sure how this would work, I'll give it a try maybe.Acrogen
S
4

Well, there is a simple way. Visit. A configuration item WTF_CSRF_ENABLED could be set to False to disable the csrf. Then everything goes as you wish.

Stirling answered 10/12, 2015 at 9:32 Comment(2)
This solved my problem, thanks. It probably got downvoted because it disables a security feature. But if the REST server is only doing token authentication and not cookie based authentication, CSRF tokens are redundant. (I welcome all comments on this!)Ruelle
Thanks very much! It works for me. using postman fot flask app testsFresco
U
3

I haven't tested that this works, but from briefly inspecting the source code it appears you have to send a GET request to the login URL with the content type set to application/json. Flask-Security responds to this request with a JSON version of the login form and that includes the token. Once you have the token you can send the POST request.

Uncritical answered 26/8, 2013 at 6:3 Comment(2)
Thanks you put on the right road. On top of making a GET request I have to pass in some json data with the request otherwise 'request.json' is empty in Flask and the '_render_json' method is not triggered. Unfortunately though this causes an error on line 45 of the source you linked because the form has no user attribute. But briefly looking at the code I think that the user attribute should be set during validation but it doesn't happen for this request. I'll have to keep digging. Thanks againAcrogen
OK I figured out the error. The 'validate_on_submit' method of Flask-WTF check first for 'is_submitted' which is false for a GET request. This prevents the 'validate' method from being called and 'user' attribute in the form from being assigned. However calling the 'validate' method manually produce a 'CSRF token missing' error and we are back at the start.Acrogen
E
0

[Writing as an an answer since I do not have enough reputation to comment]

I have run into exact same problem as Jacopo, in that - request.json is empty and thus get_auth_token() is not triggered.

BTW - Flask Security documentation says :

Token based authentication is enabled by retrieving the user auth token by performing an HTTP POST with the authentication details as JSON data against the authentication endpoint.

So I tried POST, not GET (Still same problem of empty request.json) I called /login with json data as :

{"email": "[email protected]", "password": "test123"}

and sent a request using Postman client in Google Chrome.

Yet, request.json is empty :(

Edit : I was able to move forward using python's request module. Details here

Epaulet answered 13/1, 2015 at 10:37 Comment(2)
Hi Mandar, I hope you solved this, but I just posted an answer which may help: https://mcmap.net/q/670210/-flask-security-csrf-tokenCloakroom
the answer you are giving in your blogpost to the startquestion (how to implement the csrf token when using json login on flask-security) is in essence: disable csrf authentication in the configuration of flask-security. Is this save to do? According to this article it is as long as you don't use cookies: "CSRF: since you are not relying on cookies, you don't need to protect against cross site requests..." But is it really just as save as using csrf tokens?Murdocca
C
0

I fought with this problem for hours last night. Here's what ended up working for me:

When I create my app instance:

app = Flask(__name__)
app.config.from_object(config_by_name[config_name])

# Create database connection object
app.db = db
app.db.init_app(app)

CsrfProtect(app)

In my /login HTML:

<meta name="csrf-token" content="{{ csrf_token() }}">

From Postman:

enter image description here

Here was an alternative I tried, and maybe this will be more successful? This is basically the same reply that the flask-security example apps give:

enter image description here

Cloakroom answered 13/1, 2015 at 16:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.