11
votes

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.

5
How did you do this in the end?kyrre
I ended up implementing the security layer myself. Sorry I don't have a better answer for you.Jacopo
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.Adversus

5 Answers

5
votes

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.

3
votes

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.

2
votes

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.

1
votes

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

0
votes

[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": "test@example.com", "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