kozter
kozter

Reputation: 45

Flask JWT-extended not recognizing access token, only when GET method is used

Hi everyone!

I'm building a website, and I'm stuck with sending a form. My problem is that it sends the access_token_cookie, but triggers the unauthorized_token_callback. I saw the request and the respone and the request contains the token, but the code pretends like it's not even there.

I can't wrap my head around it, and I need some good tips.

Here is the JWT configuration, the secret keys are not included in this snippet:

application.config['JWT_TOKEN_LOCATION'] = ['cookies', 'headers', 'json', 'query_string']
application.config['JWT_COOKIE_SECURE'] = False #! Needs to be changed in production
application.config['JWT_COOKIE_CSRF_PROTECT'] = True
application.config['JWT_CSRF_CHECK_FORM'] = True
application.config['JWT_COOKIE_SAMESITE'] = 'Strict'
application.config['CSRF_COOKIE_NAME'] = 'csrf_token'
application.config['JWT_ACCESS_CSRF_HEADER_NAME'] = ['X-CSRF-TOKEN-ACCESS', 'X-CSRFToken']
application.config['JWT_CSRF_IN_COOKIES'] = True

I'm using a custom decorator, that restricts the user access based on the role of the user:

def Xrole_required(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        verify_jwt_in_request()
        role = dth.get_user_by_email(get_jwt_identity()).roles.role_name
        if(role != 'required_role'):
            flash('Xrole required', 'error')
            return Response(render_template('404.html', title='Not Logged In'), 404, mimetype='text/html')
        else:
            return fn(*args, **kwargs)
    
    return wrapper

Whenever I remove the decorator, it redirects me with no problem, but using it says that I'm not logged in. I use this decorator on other places and there it works perfectly fine.

The interesting thing is, that this works on every GET request, but it does not, on other methods. Here is the form, so you can see it:

<form class="form-control" action="{{ url_for('MyEndpoint_name', show='active') }}" method="POST">
  <div class="row pt-2">
    <label for="desc" class="required">Description</label>
    <input id="desc" name="desc" type="text" required class="shadow-sm rounded border">
  </div>
  <div class="row pt-2">
    <label>Text</label>
    <input type="text" class="shadow-sm rounded border">
  </div>
  <div class="row pt-2">
    <label>Number</label>
    <input type='number' value="0" min="0" class="shadow-sm rounded border">
  </div>
    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
    <button type="submit" class="btn btn-primary">Submit</button>
</form>

And here is the resource that handles the request:

class MyResource(Resource):
    
    @Xrole_required
    def post(self, show):
        #here comes the code, this is only for test
        return redirect(url_for('/redirect_page'), 303)

Did I make a mistake in here somewhere, or did I miss some crucial part of the documentation?

Please note, that I don't want to use XMLHttpRequest, because it would be an overkill, I have a lot of js already and these are simple forms.

Upvotes: 0

Views: 727

Answers (1)

vimalloc
vimalloc

Reputation: 4167

You are probably running into a CSRF problem. Make sure to set JWT_CSRF_CHECK_FORM to True and include the CRSF token in a hidden field in your form by using https://flask-jwt-extended.readthedocs.io/en/stable/api/#flask_jwt_extended.get_csrf_token

Upvotes: 1

Related Questions