Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@jwt.expired_token_loader decorator not trigerring callback function to catch ExpiredSignatureError('Signature has expired') #83

Closed
rodrigondec opened this issue Sep 1, 2017 · 4 comments

Comments

@rodrigondec
Copy link

rodrigondec commented Sep 1, 2017

I'm using flask-restplus framework with blueprints and namespaces.

I copy-pasted the code from the tutorial/documentation seen on http://flask-jwt-extended.readthedocs.io/en/latest/changing_default_behavior.html

as the image below show

image

But it always rise the error instead of triggering the callback function, as the log indicates

ERROR in app [/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/flask/app.py:1560]:
Exception on /core/usuario [GET]
Traceback (most recent call last):
  File "/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/flask_restplus/api.py", line 313, in wrapper
    resp = resource(*args, **kwargs)
  File "/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/flask/views.py", line 84, in view
    return self.dispatch_request(*args, **kwargs)
  File "/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/flask_restplus/resource.py", line 44, in dispatch_request
    resp = meth(*args, **kwargs)
  File "/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/flask_restplus/marshalling.py", line 101, in wrapper
    resp = f(*args, **kwargs)
  File "/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/flask_jwt_extended/view_decorators.py", line 35, in wrapper
    jwt_data = _decode_jwt_from_request(request_type='access')
  File "/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/flask_jwt_extended/view_decorators.py", line 203, in _decode_jwt_from_request
    decoded_token = _decode_jwt_from_headers()
  File "/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/flask_jwt_extended/view_decorators.py", line 153, in _decode_jwt_from_headers
    identity_claim=config.identity_claim
  File "/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/flask_jwt_extended/tokens.py", line 98, in decode_jwt
    data = jwt.decode(encoded_token, secret, algorithms=[algorithm])
  File "/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/jwt/api_jwt.py", line 90, in decode
    self._validate_claims(payload, merged_options, **kwargs)
  File "/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/jwt/api_jwt.py", line 120, in _validate_claims
    self._validate_exp(payload, now, leeway)
  File "/home/rodrigondec/Documents/Prog/my3r/l_env/lib/python3.5/site-packages/jwt/api_jwt.py", line 161, in _validate_exp
    raise ExpiredSignatureError('Signature has expired')
jwt.exceptions.ExpiredSignatureError: Signature has expired
127.0.0.1 - - [01/Sep/2017 04:10:09] "GET /core/usuario HTTP/1.1" 500 -

is it because my flask aplication doesn't have @app.route('/') and instead uses @flask-resplus.namespace.route('/') ? How can i achieve the desired result?

@rodrigondec
Copy link
Author

I found a workaround to use the flask-restplus errorhandler as indicated on http://flask-restplus.readthedocs.io/en/stable/errors.html.

But shouldn't the callback function work 'catching' the error?

@vimalloc
Copy link
Owner

vimalloc commented Sep 1, 2017

From what I can tell, the problem stems from flask-restplus trying to implement it's own error handlers in an api decorated class, and any exceptions raised in that class get caught by their error handlers and never make it back to the native flask errorhandlers, which is what this extension uses. This will be a problem with every error that this extension returns, they will all be eaten by flask-restplus, not just the ones you are trying to override.

I'm not sure why they went that route, but there isn't much I can do about it in my extension. Breaking native flask functionality does not make for an extension that plays nice with others. I pinged them about this issue, and if they get back to me with how it could be resolved I'll make sure to update this. In the mean time, the only things I could think to do would be to either:

  • dump flask-restplus (personally for my REST apis, I use either swagger + connexion or normal flask blueprints and routes with marshmallow for request parsing)
  • extend flask-restplus by subclassing the Api object and making sure uncaught exceptions make it back to the flask errorhandlers, or
  • try do some magic to convert all registered flask errorhanders into Api errorhandlers at runtime.

@vimalloc
Copy link
Owner

I am adding this here as well as #86 for completeness sake.

I've thought of a better way to solve this. It is very much a hack, and I still think flask-restplus should fix their extension so that it does not break native flask features, but it should get you up and going safer then how you have it handled above.

It looks like the errorhandler method for restplus uses the same signature that flask error handler does, so you could take advantage of duck typing and access this internal method to set the errors on the restplus level: https://github.com/vimalloc/flask-jwt-extended/blob/master/flask_jwt_extended/jwt_manager.py#L81

from flask import Flask
from flask_jwt_extended import JWTManager
from flask_restplus import Api

app = Flask(__name__)
app.config['JWT_SECRET_KEY'] = 'super-secret'  # Change this!
jwt = JWTManager(app)
api = Api()

# This is where the duck typing magic comes in
jwt._set_error_handler_callbacks(api)

This would obviously be prone to break if I changed how the underlying part of this extension worked, as you are accessing a private method that doesn't have any guarantees on it, but I do not see any reason why that method would change in the foreseeable future, and this would insure that any new or changed error handles in this extension would get properly set on the flask-restplus extension.

Hope this helps :)

@vimalloc
Copy link
Owner

I'm going to close this issue. Future discussions of it will continue in #86

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants