-
-
Notifications
You must be signed in to change notification settings - Fork 239
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
Errors (i.e. 401) not returned #86
Comments
Now, I do see that default handlers are registered (in _set_error_handler_callbacks). But for some reason they never executed in my configuration |
I think it is related to the fact that I am using Restplus Api object with Flask Blueprint, so errorhandler should be used with |
Eventually, I recreated all the error handlers manually to solve it:
But I think this case should be handled internally somehow |
It is an error with rest plus breaking native flask error handlers. Look at #83 for the details. |
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 :) |
I tried it - its not working
…On Oct 10, 2017 6:22 PM, "Landon" ***@***.***> wrote:
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 Flaskfrom flask_jwt_extended import JWTManagerfrom 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 :)
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#86 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ACU-vDf7fm8v8FHICqwVt4bpRisEbjIrks5sq4uzgaJpZM4PyP7J>
.
|
It works for me if I try the following. Are you doing something different?: from flask import Flask, jsonify, request
from flask_jwt_extended import (
JWTManager, jwt_required, create_access_token,
get_jwt_identity
)
from flask_restplus import Resource, Api
app = Flask(__name__)
app.config['JWT_SECRET_KEY'] = 'super-secret' # Change this!
jwt = JWTManager(app)
api = Api(app)
jwt._set_error_handler_callbacks(api)
@api.route('/hello')
class HelloWorld(Resource):
@jwt_required
def get(self):
return {'hello': 'world'}
@api.route('/login')
class Login(Resource):
def post(self):
username = request.json.get('username', None)
password = request.json.get('password', None)
if username != 'test' or password != 'test':
return jsonify({"msg": "Bad username or password"}), 401
access_token = create_access_token(identity=username)
return {'access_token': access_token}
if __name__ == '__main__':
app.run(debug=True) And using it: $ http GET :5000/hello
HTTP/1.0 401 UNAUTHORIZED
Content-Length: 44
Content-Type: application/json
Date: Tue, 10 Oct 2017 16:06:28 GMT
Server: Werkzeug/0.12.2 Python/3.6.2
{
"msg": "Missing Authorization Header"
}
$ http POST :5000/login username=test password=test
HTTP/1.0 200 OK
Content-Length: 302
Content-Type: application/json
Date: Tue, 10 Oct 2017 16:06:30 GMT
Server: Werkzeug/0.12.2 Python/3.6.2
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDc2NTI0OTAsImlhdCI6MTUwNzY1MTU5MCwibmJmIjoxNTA3NjUxNTkwLCJqdGkiOiI4YWRjYzQyOS02MmE0LTRlNTAtYjhhZS05MmU0MTA4YTUyZDMiLCJpZGVudGl0eSI6InRlc3QiLCJmcmVzaCI6ZmFsc2UsInR5cGUiOiJhY2Nlc3MifQ.ixGtDywN2SVyBHMeSLXZq8g0fs0VwgbIARUXP8CaITQ"
}
$ http GET :5000/hello Authorization:"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDc2NTI0OTAsImlhdCI6MTUwNzY1MTU5MCwibmJmIjoxNTA3NjUxNTkwLCJqdGkiOiI4YWRjYzQyOS02MmE0LTRlNTAtYjhhZS05MmU0MTA4YTUyZDMiLCJpZGVudGl0eSI6InRlc3QiLCJmcmVzaCI6ZmFsc2UsInR5cGUiOiJhY2Nlc3MifQ.ixGtDywN2SVyBHMeSLXZq8g0fs0VwgbIARUXP8CaITQ"
HTTP/1.0 200 OK
Content-Length: 25
Content-Type: application/json
Date: Tue, 10 Oct 2017 16:06:49 GMT
Server: Werkzeug/0.12.2 Python/3.6.2
{
"hello": "world"
} |
@vimalloc Just wanted to let you know that I ran into this as well. I also tried the solution you posted and didn't have any luck. I am guessing one of two things is going on:
I did some digging to figure out where this was going awry in Restplus and you can see the link above where I submitted it upstream. The upstream issue links to a gist, in case you'd like a small test-case to play with. |
Thanks for your work on this! Hopefully this will lead to changes in flask-restplus where native flask error handlers will just work. In the mean time, I tried to duplicate the example in your gist to check the error handler work around. I wasn't able to get the gist working by itself, it couldn't find the route for the restfplust api for some reason. Instead of spending time debugging that, I created a new example using (afaict) the same type of setup that you have in the gist. from flask import Flask, Blueprint
from flask_jwt_extended import jwt_required, JWTManager, create_access_token
from flask_restplus import Api, Namespace, Resource
app = Flask(__name__)
app.config['SECRET_KEY'] = "test"
jwt = JWTManager(app)
api_v1 = Blueprint('api', __name__)
api = Api(api_v1, version='1.0', title='Todo API', description='A simple TODO API')
# This is the hack I added to get the error handlers to work with restplus
jwt._set_error_handler_callbacks(api)
ns = api.namespace('todo', description='TODO operations')
@ns.route('/access')
class ProductAccess(Resource):
@jwt_required
def post(self):
return "", 200
def get(self):
return "", 200
@app.route("/token")
def token():
return create_access_token(identity="test")
if __name__ == '__main__':
app.register_blueprint(api_v1)
print(app.url_map)
app.run(debug=True) Doing some testing on this, it looks like your guess about python2 and python3 was correct. This works correctly under python3 but not under python2: http POST :5000/todos/access
HTTP/1.0 401 UNAUTHORIZED
Content-Length: 44
Content-Type: application/json
Date: Wed, 18 Oct 2017 18:16:04 GMT
Server: Werkzeug/0.12.2 Python/3.6.2
{
"msg": "Missing Authorization Header"
}
http POST :5000/todos/access
HTTP/1.0 500 INTERNAL SERVER ERROR
Content-Length: 43
Content-Type: application/json
Date: Wed, 18 Oct 2017 18:24:06 GMT
Server: Werkzeug/0.12.2 Python/2.7.14
{
"message": "Internal Server Error"
} I'm not sure why it is working in python3 and not python2 yet. Hopefully I can find some time to dig into this later this week. That said, I think the ideal situation would still be to see flask-restplus update their extension so it passes back errors to the flask error handlers. Cheers! EDIT: Here is the stacktrace for python2
|
The problem that I am seeing in python2 with my hack applied stems from here: My error handlers return a flask response, not a python dictionary, which ends up in This in turn causes the original app error handler to be run here
Funny enough, in python3 the exact same sequence happens. By registering our error handlers on the flask-restplus api object, we aren't actually having flask-restplus serve our error handlers. We are just setting up a situation where an exception is raised in the flask-restplus error handling, and that triggers flask-restplus to kick the error back up to the native flask error. I'm guessing the reason why this works with python3 and not python2 boils down to the subtle differences in how exceptions work between them. Regardless, what I would really like to see is flask-restplus kicking the exception back up to the native flask error handlers, so that no magic needs to be done to use these extensions together. |
Flask-restplus and python2.7 is also used in my project. Is there a solution to this problem now? |
I am not aware of any changes that have not been outlined in this ticket. If flask-restful is still bypassing the built in flask error handlers (make sure you have |
I am not entirely sure what exactly the issue is but, i came across the very same issue on Python 3.6.7, today while testing a simple CRUD app that has Flask-Restful + SQLAlchemy + Flask-JWT-extended. If i keep For eg. class Development:
DEBUG = True
SECRET_KEY = "changeme"
JWT_ACCESS_TOKEN_EXPIRES = False
SQLALCHEMY_DATABASE_URI = 'postgresql://postgres:postgres'\
'@localhost/examples'
# Suppress SQLALCHEMY_TRACK_MODIFICATIONS overhead warning
SQLALCHEMY_TRACK_MODIFICATIONS = False
class Production(Development):
DEBUG = False
SECRET_KEY = "sup3rs3cr3tk3yf0rPr0duct10N"
SQLALCHEMY_DATABASE_URI = os.getenv(
'SQLALCHEMY_DATABASE_URI',
'postgresql://localhost/examples'
)
JWT_ACCESS_TOKEN_EXPIRES = timedelta(7)
class Testing(Development):
DEBUG = True
TESTING = True
SECRET_KEY = "testsecret"
JWT_ACCESS_TOKEN_EXPIRES = False
SQLALCHEMY_DATABASE_URI = 'postgresql://postgres:postgres'\
'@localhost/examples_test'
# Suppress SQLALCHEMY_TRACK_MODIFICATIONS overhead warning
SQLALCHEMY_TRACK_MODIFICATIONS = False As you can see, I kept Completely out of curiosity, I changed the Any idea why this happens, and where would the culprit to this cause be? 🤔 Here is a traceback of the 500: Traceback (most recent call last):
File "/home/madboy/.virtualenvs/examples-rest/lib/python3.6/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/home/madboy/.virtualenvs/examples-rest/lib/python3.6/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/home/madboy/.virtualenvs/examples-rest/lib/python3.6/site-packages/flask_restful/__init__.py", line 480, in wrapper
resp = resource(*args, **kwargs)
File "/home/madboy/.virtualenvs/examples-rest/lib/python3.6/site-packages/flask/views.py", line 88, in view
return self.dispatch_request(*args, **kwargs)
File "/home/madboy/.virtualenvs/examples-rest/lib/python3.6/site-packages/flask_restful/__init__.py", line 595, in dispatch_request
resp = meth(*args, **kwargs)
File "/home/madboy/.virtualenvs/examples-rest/lib/python3.6/site-packages/flask_jwt_extended/view_decorators.py", line 102, in wrapper
verify_jwt_in_request()
File "/home/madboy/.virtualenvs/examples-rest/lib/python3.6/site-packages/flask_jwt_extended/view_decorators.py", line 31, in verify_jwt_in_request
jwt_data = _decode_jwt_from_request(request_type='access')
File "/home/madboy/.virtualenvs/examples-rest/lib/python3.6/site-packages/flask_jwt_extended/view_decorators.py", line 284, in _decode_jwt_from_request
raise NoAuthorizationError(errors[0])
flask_jwt_extended.exceptions.NoAuthorizationError: Missing Authorization Header |
Yeah, for whatever reason when you are using flask-restful you will need to set |
Wow, super duper quick reply 👏 |
It works for me in python 3.6 with Blueprints |
I had similar issue, and after a little bit of digging through code I raised issue in Flask-RESTful about this: flask-restful/flask-restful#796 I am not sure that enabling PROPAGATE_EXCEPTIONS in production environment is the right way to go, since that way we would leave any actual exceptions without registered handlers completely unhandled. |
Fix the Default callbacks are all provided, but never returned for flask_jwt Solution: vimalloc/flask-jwt-extended#86
I'm not getting any luck with the What am I missing?
|
@Jonyorker Flask restplus has a different issue that causes problems with native flask errorhandlers: noirbizarre/flask-restplus#340. You can work around that for the time being with the details here: #86 (comment) |
RIght, but I've added in the jwt._set_error_handler_callbacks(api) and the progapge_exceptions and I'm still getting the error 500 instead of whatever 40* it should be. |
Are you using python3? That work around doesn't work for python2 for whatever reason. |
3.7 |
Hrm, I'm not sure right off hand then. I'll try to take a closer when I have some downtime 👍 |
I appreciate it. Up for whatever you need me to provide you to debug it. Thanks! |
I agree that this is not the best solution although it works. Are there any plans to fix this or is PROPAGATE_EXCEPTIONS the official solution now? |
@matusbielik As the |
Setting |
Have you gone over the other solutions outlined in this ticket, for example if you are using flask-restplus? If none of them are working for you, please open a new ticket with all the details you can. Specifically, what version of flask and flask-jwt-extension you are using, what other flask extensions you are running, and a minimum example which demonstrates this behavior. |
I'm using This is my main file
|
Are you using python3? The work around linked above with |
Yes, I'm using |
I'm going to lock this issue for now, as adding more comments on here only makes it harder to find the helpful solutions linked in this ticket. As a summary, this issue is not caused by flask-jwt-extended, but rather by other flask extensions not working correctly with flask errorhandlers, a native feature in flask. Because of this, there is not a lot I can do on my end to fix their issues, I can only help try to find workarounds. If you are running into this error, I would start by looking at these comments: #86 (comment) : If you are still having issues, please open a new issue with a minimal complete and verifiable example to help me dig into this further. I will make sure to link any updates back here so the information is easily available. Thanks! 👍 |
It looks to me that this part is not yet implemented:
The upper code raises many different exceptions, but I don't see any code returning the errors (my own default error handling of restplus triggers 500 error every time).
The documentation states that:
Default callbacks are all provided, but never returned.
Am I wrong?
Thanks,
Meir Tseitlin
The text was updated successfully, but these errors were encountered: