[Flask] Proper way to refresh Flask-WTF CSRF token?

Scott Woodstock sidwoodstock at gmail.com
Wed Oct 9 13:05:00 EDT 2019


What ho!

A page of my Flask app functions as a single-page web app using mostly ajax
requests via jQuery. After being on the page for awhile, inevitably the
CSRF token will expire and subsequent HTTP POST requests will fail (Bad
Request 400).

The docs provide the method of adding the token to the header, but this
scenario isn't mentioned.
https://flask-wtf.readthedocs.io/en/stable/csrf.html#

Rather than increase the token life and hope users don't stay on the page
longer than its duration, I opted for creating a jquery / javascript
heartbeat to request a new token and an endpoint to generate one and return
it.

This seems to work how I want it to, but since this scenario is not
mentioned anywhere in any docs that I could find, I'm wondering if this is
actually accepted practice.

Please advise!
--------------------
# views.py

from flask_wtf.csrf import generate_csrf
...
@app.route('/tools/_refresh_csrf/', methods=['GET'])
@roles_accepted('admin', 'copy', 'client')
def csrf_refresh():
    token = generate_csrf()
    return jsonify(token)


// template.html

    var csrf_token = '{{ csrf_token() }}'; // initial token

    // refresh the csrf token every 30 minutes
    var heartbeat = setInterval(function () {
      $.ajax({
        url: '{{ url_for('csrf_refresh') }}',
        type: 'GET',
        success: function (response) {
          csrf_token = response;
          console.log('new token', csrf_token)
        },
        error: function (jqXHR, textStatus, errorThrown) {
          console.log(jqXHR, textStatus, errorThrown);
          alert('Connection with server lost! Please refresh the page.')
        },
        dataType: "json",
        contentType: "application/json"
      });
    }, 30 * 60 * 1000);

    // apply the csrf token before each request
    $.ajaxSetup({
      beforeSend: function(xhr, settings) {
        if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) &&
!this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrf_token);  // insert
custom header
        }
      },
    });

--------------------
Thank you all for your time!

Scott Woodstock
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/flask/attachments/20191009/a8ff6e3d/attachment.html>


More information about the Flask mailing list