Closed gwvt closed 7 years ago
Any help with this would be much appreciated. Thanks!
Hey @gwvt can you post the (non secret) details of the config file? If I have a fully working example, I will look at this tonight.
Sorry for the delay!
Great, thanks so much! Here's the simplified config file:
class Config(object):
DEBUG = False
TESTING = False
HOST_NAME = 'localhost:5000'
DATABASE_URI = 'postgresql+psycopg2://localhost:5432/database'
SQLALCHEMY_TRACK_MODIFICATIONS = False
CORS_ENABLED = False
class DevelopmentConfig(Config):
DEBUG = True
CORS_ENABLED = True
Let me know if you need any other info.
Or here's a complete self-contained application:
from flask import Flask, request
from flask_restful import Api, Resource
from flask_cors import CORS
app = Flask(__name__)
api = Api(app)
CORS(app, origins="http://127.0.0.1:8080", allow_headers=[
"Content-Type", "Authorization", "Access-Control-Allow-Credentials"],
supports_credentials=True)
@app.before_request
def authorize_token():
if request.endpoint != 'token':
try:
auth_header = request.headers.get("Authorization")
if "Bearer" in auth_header:
token = auth_header.split(' ')[1]
if token != '12345678':
raise ValueError('Authorization failed.')
except Exception as e:
return "401 Unauthorized\n{}\n\n".format(e), 401
class GetToken(Resource):
def post(self):
token = '12345678'
return token # token sent to client to return in subsequent
# requests in Authorization header
# requires authentication through before_request decorator
class Test(Resource):
def get(self):
return {"test": "testing"}
api.add_resource(GetToken, '/token', endpoint='token')
api.add_resource(Test, '/test', endpoint='test')
if __name__ == '__main__':
app.run()
Same thing, getting 'Response to preflight request doesn't pass access control check' error message when the api is called from localhost:8080, sending a request to endpoint '/test' with header 'Authorization: Bearer 12345678'.
Sorry for the delay, I finally got a chance to look at this properly.
It looks like the issue is with your origin header. What is the 'Origin' you are sending the CORS request from? Your configuration will only allow browsers to issue a CORS request from "http://127.0.0.1:8080" to your flask-cors app (running by default on localhost:5000).
Example: If a browser were currently looking at an html page on "http://127.0.0.1:8080", and the JS on that page issued an XHR to e.g. localhost:5050/token, they would send a request like this, and receive a similarly succesful response.
➜ flask-cors-test curl --include -X GET http://127.0.0.1:5000/test --header Origin:http://127.0.0.1:8080 --header 'Authorization: Bearer 12345678'
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 20
Access-Control-Allow-Origin: http://127.0.0.1:8080
Access-Control-Allow-Credentials: true
Server: Werkzeug/0.12.1 Python/2.7.13
Date: Sat, 22 Apr 2017 20:47:26 GMT
{"test": "testing"}
But, if instead your browser is pointing to something even slightly different, e.g. localhost:8080 (which would resolve to the same thing on your machine), the browser will see the issue you are reporting. The browser will issue a command similar to this, and receive a non-cors response:
➜ flask-cors-test curl --include -X GET http://127.0.0.1:5000/test --header Origin:http://localhost:8080 --header 'Authorization: Bearer 12345678'
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 20
Server: Werkzeug/0.12.1 Python/2.7.13
Date: Sat, 22 Apr 2017 20:49:33 GMT
{"test": "testing"}
Does that make sense? Sorry for the delay again!
Thank you very much for your reply. That's my mistake in the code specifying host of origin, but that actually isn't the issue. With curl sending a normal GET request, everything works, but the issue is with the preflight request sent by the browser via the OPTIONS method with headers. I tried both with Flask-Restful as well as a standard Flask application, with the same results (see below for code).
The endpoints without the before_request decorator that checks the JWT token work as expected, but sending a request to the '/test' endpoint with the before_request decorator returns this error message in Chrome:
XMLHttpRequest cannot load http://127.0.0.1:5000/test. Response for preflight has invalid HTTP status code 401
The response body is:
401 Unauthorized
argument of type 'NoneType' is not iterable
The headers for the request and response are:
General:
Request URL:http://127.0.0.1:5000/test
Request Method:OPTIONS
Status Code:401 UNAUTHORIZED
Remote Address:127.0.0.1:5000
Referrer Policy:no-referrer-when-downgrade
Response Headers
view source
Response Headers:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:authorization
Access-Control-Allow-Methods:DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT
Access-Control-Allow-Origin:http://localhost:8080
Content-Length:62
Content-Type:text/html; charset=utf-8
Date:Mon, 24 Apr 2017 14:51:22 GMT
Server:Werkzeug/0.12.1 Python/2.7.10
Request Headers
view source
Request Headers:
Accept:*/*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:authorization
Access-Control-Request-Method:GET
Connection:keep-alive
DNT:1
Host:127.0.0.1:5000
Origin:http://localhost:8080
Referer:http://localhost:8080/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Name
Is there something I'm missing is setting up CORS with the before_request decorator? Any guidance will be very much appreciated. Thank you!
*
Code for self-contained application, Flask-Restful:
from flask import Flask, request
from flask_restful import Api, Resource
from flask_cors import CORS
app = Flask(__name__)
api = Api(app)
CORS(app, origins="http://localhost:8080", allow_headers=[
"Content-Type", "Authorization", "Access-Control-Allow-Credentials"],
supports_credentials=True, intercept_exceptions=False)
@app.before_request
def authorize_token():
if request.endpoint == 'test':
try:
auth_header = request.headers.get("Authorization")
if "Bearer" in auth_header:
token = auth_header.split(' ')[1]
if token != '12345678':
raise ValueError('Authorization failed.')
except Exception as e:
return "401 Unauthorized\n{}\n\n".format(e), 401
class GetToken(Resource):
def post(self):
token = '12345678'
return token
# requires authentication through before_request decorator
class Test(Resource):
def get(self):
return {"test": "testing"}
class TestHeaders(Resource):
def get(self):
auth_header = request.headers.get("Authorization")
token = auth_header.split(' ')[1]
return token
api.add_resource(GetToken, '/token', endpoint='token')
api.add_resource(Test, '/test', endpoint='test')
api.add_resource(TestHeaders, '/headers', endpoint='headers')
if __name__ == '__main__':
app.run(debug=True)
And the equivalent plain Flask application:
from flask import Flask, request
from flask_cors import CORS
app = Flask(__name__)
CORS(app, origins="http://localhost:8080", allow_headers=[
"Content-Type", "Authorization", "Access-Control-Allow-Credentials"],
supports_credentials=True, intercept_exceptions=False)
@app.before_request
def authorize_token():
if request.endpoint == 'test':
try:
auth_header = request.headers.get("Authorization")
if "Bearer" in auth_header:
token = auth_header.split(' ')[1]
if token != '12345678':
raise ValueError('Authorization failed.')
except Exception as e:
return "401 Unauthorized\n{}\n\n".format(e), 401
@app.route('/token', methods=['POST'])
def get_token():
token = '12345678'
return token
# requires authentication through before_request decorator
@app.route('/test', methods=['GET'])
def test():
return 'test'
@app.route('/headers', methods=['GET'])
def test_headers():
auth_header = request.headers.get("Authorization")
token = auth_header.split(' ')[1]
return token
if __name__ == '__main__':
app.run(debug=True)
This looks to be an application issue, auth_header
is None, so:
if "Bearer" in auth_header:
is failing.
Right. The test_headers() function assigns the value of the Authorization header to auth_header, while the test() function that is 'protected' by the before_request decorator and authorize_token function does not.
I figured out that the problem was that the authorize_token function requires a test to run the function only on the passed GET method, not the preflight request, so this now works:
@app.before_request
def authorize_token():
if request.endpoint == 'test':
try:
if request.method != 'OPTIONS': # <-- required
auth_header = request.headers.get("Authorization")
if "Bearer" in auth_header:
token = auth_header.split(' ')[1]
if token != '12345678':
raise ValueError('Authorization failed.')
except Exception as e:
return "401 Unauthorized\n{}\n\n".format(e), 401
I've encountered same problem and this page helps a lot.Thx!
You are my savior @gwvt . Thank you very much
for what was the purpose of request.method == 'OPTIONS'
in Flask Middleware, before...request
and after...request
, 🤔 because it fires up 2 times, the first fire up has method of OPTIONS
and the second one is the <DEFAULT METHOD>
you've used 🤔
I'm trying to use flask-cors for the development configuration for a flask-restful api, simplified below:
But whatever I try I always get the error 'Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.'
Without the JWT auth piece, everything else works fine. (And the JWT auth works fine without flask-cors.) Seems like the hangup is something with using flask-cors with the before_request decorator (?).
Any suggestions?