I have a middleware in my Flask app that is used to authenticate a JSON Web Token coming in the header of the request and is checking to verify it, below is my middleware class:
class AuthMiddleware(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
path = environ.get('PATH_INFO')
if path != '/authenticate' or path != '/token':
token = environ.get('HTTP_X_ACCESS_TOKEN')
verfied_token = verify_token(token)
if verfied_token is False:
abort(401)
elif verfied_token is True:
# payload = get_token_payload(token)
# check_permissions(payload)
pass
return self.app(environ, start_response)
verify_token() is a function that will return True or False, and if it returns False, I want it to abort with an error 401. However, it aborts with an error 500:
127.0.0.1 - - [25/Mar/2015 11:37:25] "POST /role HTTP/1.1" 500 -
Error on request:
Traceback (most recent call last):
File "/ENV/lib/python2.7/site-packages/werkzeug/serving.py", line 180, in run_wsgi
execute(self.server.app)
File "/ENV/lib/python2.7/site-packages/werkzeug/serving.py", line 168, in execute
application_iter = app(environ, start_response)
File "/ENV/lib/python2.7/site-packages/flask/app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/middleware.py", line 24, in __call__
return self.app(environ, start_response)
File "/ENV/lib/python2.7/site-packages/werkzeug/exceptions.py", line 605, in __call__
raise self.mapping[code](*args, **kwargs)
BadRequest: 400: Bad Request
In my views, I abort a 401 like it should, but here it seems to be a problem. What should I do?
The middleware you've shown runs some other code and then calls the wrapped Flask application. However, abort raises exceptions that Flask handles, but aren't handled by WSGI directly. Since you're not in the Flask application yet, it can't handle the exception.
A much easier way would be to do this check inside the Flask app. Create a before_request handler that does basically the same thing as the middleware, except you can use flask.request rather than needing to parse the path and headers yourself.
from flask import request, abort
#app.before_request
def check_auth_token():
if request.path in ('/authenticate', '/token'):
return
token = request.headers.get('X-ACCESS-TOKEN')
if not verify_token(token):
abort(401)
check_permissions(get_token_payload(token))
If you do want to use WSGI middleware for this, you need to create the response yourself. Conveniently, Werkzeug's exceptions behave like WSGI applications, so it's straightforward to use them.
from werkzeug.exceptions import Unauthorized
# in place of abort(401) in the middleware
return Unauthorized()(environ, start_response)
You can also use abort still by catching the exceptions it raises (which, again, are WSGI applications).
from werkzeug.exceptions import abort, HTTPException
# werkzeug.exceptions.abort is the same as flask.abort
try:
abort(401)
except HTTPException as e:
return e(environ, start_response)
Related
I have my flask-restful app.py which contains all of my main functions. I have created a server.py file as instructed from here: https://auth0.com/docs/quickstart/backend/python
In my app.py file from server i import AuthError and requires_auth. I have then put #requires_auth in front of my functions.
When I have a valid jwt, it works perfectly. When the jwt is not valid it fails. Failing is good, because the requests shouldn't work. But the response i get from my api is "Internal Server Error" rather than the detailed response in the raise AuthError section in the server.py file.
I get 2 errors:
Traceback (most recent call last):
File "C:\Users\ME\code\server.py", line 88, in decorated
issuer="https://"+AUTH0_DOMAIN+"/"
File "C:\Users\ME\lib\site-packages\jose\jwt.py", line 150, in decode
options=defaults)
File "C:\Users\ME\lib\site-packages\jose\jwt.py", line 457, in _validate_claims
_validate_exp(claims, leeway=leeway)
File "C:\Users\ME\lib\site-packages\jose\jwt.py", line 299, in _validate_exp
raise ExpiredSignatureError('Signature has expired.')
jose.exceptions.ExpiredSignatureError: Signature has expired.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\ME\lib\site-packages\flask\app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\ME\lib\site-packages\flask\app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\Users\ME\lib\site-packages\flask_restful\__init__.py", line 480, in wrapper
resp = resource(*args, **kwargs)
File "C:\Users\ME\lib\site-packages\flask\views.py", line 88, in view
return self.dispatch_request(*args, **kwargs)
File "C:\Users\ME\lib\site-packages\flask_restful\__init__.py", line 595, in dispatch_request
resp = meth(*args, **kwargs)
File "C:\Users\ME\code\server.py", line 92, in decorated
"description": "token is expired"}, 401)
server.AuthError: ({'code': 'token_expired', 'description': 'token is expired'}, 401)
How do i get the AuthError as the response to the call, rather than just my Internal Server Error?
Thanks!
There is an issue with this specific tutorial in Auth0, it instructs you to include the error handler in auth.py:
#app.errorhandler(AuthError)
def handle_auth_error(ex):
response = jsonify(ex.error)
response.status_code = ex.status_code
return response
Instead, you have to include this handler in your app.py, where you actually use #requires_auth.
Notice that to do so, you need to add relevant imports:
from auth import AuthError
from flask import jsonify
Notice: To be able to import from auth.py you need to add an empty file __init__.py in the same directory.
Try setting app.config[“PROPAGATE_EXCEPTIONS”] = True
Uou could maybe use an errorhandler to explicitly catch those errors and return some explicit json based on them.
This question already has answers here:
Return JSON response from Flask view
(15 answers)
Closed 6 years ago.
Back again here with a flask question. I am a beginner and learn some awesome things from reddit (how to legit code).
Here is my code that I have grabbed certain information from an API that I am now trying to host locally through flask.
from flask import Flask, render_template
import httplib
import json
app = Flask(__name__)
#app.route('/')
def index():
connection = httplib.HTTPConnection('api.football-data.org')
headers = {'X-Auth-Token': 'this is my api token here', 'X-Response-Control': 'minified'}
connection.request('GET', '/v1/competitions/426/leagueTable', None, headers)
response = json.loads(connection.getresponse().read().decode())
return response
if __name__ == '__main__':
app.run()
When I run 127.0.0.1:5000 I get:
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
Here is what my server is telling me!
MacBooks-MBP:Football macbookpro13$ python Footy_Web.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
[2016-12-20 13:58:17,493] ERROR in app: Exception on / [GET]
Traceback (most recent call last):
File "/Library/Python/2.7/site-packages/flask/app.py", line 1988, in wsgi_app
response = self.full_dispatch_request()
File "/Library/Python/2.7/site-packages/flask/app.py", line 1642, in full_dispatch_request
response = self.make_response(rv)
File "/Library/Python/2.7/site-packages/flask/app.py", line 1746, in make_response
rv = self.response_class.force_type(rv, request.environ)
File "/Library/Python/2.7/site-packages/werkzeug/wrappers.py", line 847, in force_type
response = BaseResponse(*_run_wsgi_app(response, environ))
File "/Library/Python/2.7/site-packages/werkzeug/wrappers.py", line 57, in _run_wsgi_app
return _run_wsgi_app(*args)
File "/Library/Python/2.7/site-packages/werkzeug/test.py", line 871, in run_wsgi_app
app_rv = app(environ, start_response)
TypeError: 'dict' object is not callable
127.0.0.1 - - [20/Dec/2016 13:58:17] "GET / HTTP/1.1" 500 -
I should mention this code works outside of the flask framework!
You need to return a valid HTTP response. To do so, you can use jsonify like below:
return jsonify(response)
Do not forget to import jsonify like this:
from flask import jsonify
jsonify() returns a flask.Response() object.
You can also use json.dumps(), but in this case you need to add a http status code and a Content-Type header to return a valid HTTP response:
return json.dumps(response), 200, {'content-type': 'application/json'}
I have made a custom middleware and if it throws exception, it is automatically handled by BaseHandler function get_response (django.core.handlers.base). My exception ObjectDoesNotExist, by default, is not handled and therefore a function handle_uncaught_exception takes care of it. But it considers all error as 500 Internal Server (even if it is a 400 Bad request) and returns default django error html template with 500 code. Here is my code:
custom_middleware.py:
class CustomMiddleware(object):
"""
custom middleware
"""
def process_request(self, request):
try:
# if agent doesn't exists then return json response
agent = Agent.objects.get(agent_token=agent_token)
except ObjectDoesNotExist as e:
raise Exception(exception_code=400, detail=e, response_msg="You are not an authorized agent", request= request_data)
This automatically is handled by BaseHandler.get_response()
So to solve the problem what I did was inherited the BaseHandler and override get_response function adding exception handler for my error and which gives a json response rather than HTML template.
base.py
from django.core.handlers.base import BaseHandler
class ExceptionHandler(BaseHandler):
def get_response(self, request):
my code
But this doesn't help as it calls the base class method even after overriding.
Here is the traceback:
File "/webapp/apps/middleware/custom_middleware.py" in process_request
49. agent = Agent.objects.get(agent_token=agent_token)
File "lib/python3.4/site-packages/django/db/models/manager.py" in manager_method
122. return getattr(self.get_queryset(), name)(*args, **kwargs)
File "lib/python3.4/site-packages/django/db/models/query.py" in get
387. self.model._meta.object_name
During handling of the above exception (Agent matching query does not exist.), another exception occurred:
File "lib/python3.4/site-packages/django/core/handlers/base.py" in get_response
123. response = middleware_method(request)
File "webapp/apps/middleware/request_log.py" in process_request
54. raise Exception(exception_code=400, detail=e, response_msg="You are not an authorized agent", request= request_data)
Any help
App Engine's python client library has made oauth flow really easy with the following decorator.
#decorator.oauth_required
But it's really not straightforward to mock/patch for unit testing. For example in the following get handler, I need to stub out oauth decorator.
from auth import decorator
class ListUsersHandler(webapp2.RequestHandler):
#decorator.oauth_required
def get(self):
self.response.write(_RenderUserListTemplate())
I have tried something like below.
from mock import patch
patch('decorator.oauth_required', lambda x: x).start()
import user
class MyTest(unittest.TestCase):
def setUp(self):
app = webapp2.WSGIApplication([('/', user.ListUsersHandler)])
self.testapp = webtest.TestApp(app)
def testListUsersHandler(self):
response = self.testapp.get('/')
self.assertTrue(('list tokens' in response))
But, what I'm seeing this error, which doesn't seem to give much clue.
Traceback (most recent call last):
File "user_test.py", line 44, in testAbc
response = self.testapp.get('/')
File "/usr/local/lib/python2.7/dist-packages/webtest/app.py", line 322, in get
expect_errors=expect_errors)
File "/usr/local/lib/python2.7/dist-packages/webtest/app.py", line 605, in do_request
res = req.get_response(app, catch_exc_info=True)
File "/google_appengine/lib/webob-1.2.3/webob/request.py", line 1292, in send
application, catch_exc_info=True)
File "/google_appengine/lib/webob-1.2.3/webob/request.py", line 1269, in call_application
return (captured[0], captured[1], app_iter, captured[2])
IndexError: list index out of range
While inside a callback function, I lose the ability to access flask.session, flask.g, or functions such as url_for(). They all throw an error saying that I'm "working outside of request context".
Debugging middleware caught exception in streamed response at a point where response headers were already sent.
Traceback (most recent call last):
File "C:\Python27\site-packages\sijax\response\streaming.py", line 136, in _process_call_chain
for string in generator:
File "C:\Python27\site-packages\sijax\response\streaming.py", line 109, in _process_callback
response = self._perform_handler_call(callback, args)
File "C:\Python27\site-packages\sijax\response\base.py", line 258, in _perform_handler_call
return callback(self, *args)
File "C:\Dropbox\Code\Python 2.7\FlaskTesting\testpage.py", line 18, in myformhandler
sql_session = flask.g.sql_session
File "C:\Python27\lib\site-packages\werkzeug\local.py", line 336, in __getattr__
return getattr(self._get_current_object(), name)
File "C:\Python27\lib\site-packages\werkzeug\local.py", line 295, in _get_current_object
return self.__local()
File "C:\Python27\lib\site-packages\flask\globals.py", line 19, in _lookup_object
raise RuntimeError('working outside of request context')
RuntimeError: working outside of request context
192.168.1.141 - - [20/Jun/2012 16:33:04] "POST /testpage HTTP/1.1" 200 -
I've been unable to find out how to get around this problem. Any help would be appreciated.
Python v2.7
Flask v0.8
Flask-Sijax v0.3
You may have a try with stream_with_context. The code example copied from http://flask.pocoo.org/docs/0.12/patterns/streaming/#streaming-with-context
from flask import stream_with_context, request, Response
#app.route('/stream')
def streamed_response():
def generate():
yield 'Hello '
yield request.args['name']
yield '!'
return Response(stream_with_context(generate()))
It would be helpful if you posted your code, but try wrapping your code like this:
with app.app_context():
# do stuff...
or maybe this:
with app.test_request_context():
# do stuff...