How to enable CORS on Google App Engine Python Server? - python

I am see the following error on Javascript console:
VM31:1 XMLHttpRequest cannot load '<some-url>'. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '<my-url>' is therefore not allowed access.
How do I enable Cross-Origin Resource Sharing with Google App Engine (Python) to access ?

You'll have to use the Access-Control-Allow-Origin http header in your yaml configuration
handlers:
- url: /
...
http_headers:
Access-Control-Allow-Origin: http://my-url
Find more under CORS Support in the docs

For a python script you can add the following line near other self.response.header lines.
self.response.headers['Access-Control-Allow-Origin'] = '*'
This worked for me. The idea was taken from a php issue listed in the notes of another answer.

For those who are wondering how to basically allow all origins for the AppEngine instance in Springboot:
use the #CrossOrigin(origins = "*") annotation on the #RestController classes your project has
or use use the same annotation above for any of your specific resource methods that has one of the #GetMapping, #PostMapping, etc annotations.
No need to set any of the handlers in the app.yaml. Actually it didn't work when changing the app.yaml file as explained in the docs
...
...
...
#SpringBootApplication
#RestController
#CrossOrigin(origins = "*") // <--- here
public class SpringbootApplication {
...
...
#GetMapping("/")
#CrossOrigin(origins = "*"). // <--- or here
public String hello() {
.....
}
}

If you want to serve a script, you cannot use Jeffrey Godwyll's answer, unfortunately. The documentation, somewhat hidden in the second sentence of the http_headers states: "If you need to set HTTP headers in your script handlers, you should instead do that in your app's code."
Another possibility is to allow your app to handle pre-flight requests by "prematurely" returning the headers. GOTCHA: If you are building a POST endpoint, make it so that it returns the allow cross site origin request headers on everything BUT your desired request method. Sometimes there may be a pre-flight GET as well (for some odd reason):
from flask import Flask, request
HEADERS = {
"Access-Control-Allow-Origin": "*",
}
app = Flask(__name__)
#app.route("/", methods=["GET", "POST"])
def main():
if request.method != "POST":
return ("", 204, HEADERS)
return "After the POST"
If you are building an app for GET only, you can instead write if request.method == "OPTIONS":..., like in the Cloud Functions documentation

Related

Flaskdance doesn't generate a HTTPS uri

I'm trying to set up Google sign-in using Flask dance for a flask based website:
from flask_dance.contrib.google import make_google_blueprint, google
blueprint = make_google_blueprint(
client_id= "CLIENT_ID",
client_secret="CLIENT_SECRET",
scope=[
"https://www.googleapis.com/auth/plus.me",
"https://www.googleapis.com/auth/userinfo.email",
]
)
app.register_blueprint(blueprint, url_prefix="/google_login")
And as the documentation suggests, I have the view set up like this:
#app.route('/google_login')
def google_login():
if not google.authorized:
return redirect(url_for("google.login"))
resp = google.get("/oauth2/v2/userinfo")
assert resp.ok, resp.text
return "You are {email} on Google".format(email=resp.json()["email"])
When I was testing I set the environment variable, OAUTHLIB_INSECURE_TRANSPORT to 1 by using
export OAUTHLIB_INSECURE_TRANSPORT=1
And now even after I've removed the environment variable, for some reason the Flaskdance seems to always resolve the URI to a http instead of HTTPS.
This is evident from the redirect uri mismatch error I'm getting (here website refers to the domain name):
The redirect URI in the request,
http://"website"/google_login/google/authorized, does not match
the ones authorized for the OAuth client.
And here are the authorized redirect URIs I've set up in my Google cloud console:
https://"website"/google_login/google/authorized
https://www."website"/google_login/google/authorized
I tried unsetting the environment variable using this command:
unset OAUTHLIB_INSECURE_TRANSPORT
What am I missing here? Any help would be appreciated.
If Flask-Dance is generating http URLs instead of https, that indicates that Flask (not Flask-Dance, but Flask itself) is confused about whether the incoming request is an https request or not. Flask-Dance has some documentation about how to resolve this problem, and the most likely cause is a proxy server that handles the HTTPS separately from your application server.
The fix is to use a middleware like werkzeug's ProxyFix to teach Flask that it's behind a proxy server. Here's how you can use it:
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1)
I had the same problem and in my case adding this to my Apache VirtualHost config solved it:
RequestHeader set X-Forwarded-Proto "https"
My Flask is running behind an Apache proxy but Nginx would also have similar issues, potentially.

App.yaml on app engine is not forwarding wildcards properly

I tried recently to enable html5mode browsing in angular with
// use the HTML5 History API
$locationProvider.html5Mode(true);
Suddenly though, when I try to go to one of my client side routes, app engine seems not to even forward my request to my webapp2 handler.
For example, localhost:8080/#/myRoute routes properly when entered directly but localhost:8080/myRoute gives me a 404.
I recognize # indicates a client side route, but as long as my request is forwarded to my index.html angular should handle the request. I didn't explicitly create a route for /#/ so it seems like it's handling those as wildcards but not other routes.
I tried these routes in my app.yaml:
- url: /
script: server.web_server.main.app
- url: .*
script: server.web_server.main.app
- url: /.*
script: server.web_server.main.app
My server.web_server.main.app routing setup looks like:
app = webapp2.WSGIApplication([
# Main SPA handler
webapp2.Route('/', MainHandler, name='main'),
], debug=True)
MainHandler never even sees a request when I don't use # and the server logs indicate indeed they did have a 404. I could understand if both # and nothing caused a 404 then my wildcards would not be working properly, but why would /#/ work and / not work if I haven't put any special routing for /#/?
Can someone explain what I'm doing wrong?
Thanks in advance!
-- Jp.
MainHandler isn't seeing a request for /myRoute because only / is being routed (within the app) to that handler, even though your app.yaml is routing requests to the app.
Try adding something like
webapp2.Route('/myRoute', MainHandler, name='myroutemain'),
to the WSGIApplication.
So the post above is totally correct, but I thought I would add an even simpler solution solution which doesn't require you to hand specify each client side route.
You can add a wildcard regex by doing the following:
class MainHandler(BaseHandler):
def get(self, opt=None):
self.render("index.html", {"title": "My Angular App"})
app = webapp2.WSGIApplication([
# Main SPA handler
webapp2.Route(r'/<:.*>', MainHandler, name='main')
], debug=True)
I didn't quite have the syntax right for the regex when I tried originally. The captured value gets passed to get which you can use if you want.

PUT request not working with web2py?

We are having some issue with Cross Origin Resource Sharing (CORS) implementation in a restfull web service on web2py.
We try to implement CORS on the server side in web2py as suggested here: ( https://groups.google.com/forum/#!msg/web2py/kSUtyNcUQGI/qfiIqfUiWLwJ )
We added following to models/0.py, (to have the response header updated before actual restfull api handler in the controler)
===============================
if request.env.http_origin:
response.headers['Access-Control-Allow-Origin'] = request.env.http_origin
response.headers['Access-Control-Allow-Origin'] = "*"
response.headers['Access-Control-Allow-Credentials'] = 'true'
response.headers['Access-Control-Max-Age'] = 86400
if request.env.request_method == 'OPTIONS':
if request.env.http_access_control_request_method:
print request.env.http_access_control_request_method
response.headers['Access-Control-Allow-Methods'] = request.env.http_access_control_request_method
if request.env.http_access_control_request_headers:
response.headers['Access-Control-Allow-Headers'] = request.env.http_access_control_request_headers
==========================
RESTful POST & GET are now working
but PUT and DELETE aren't because preflight http OPTIONS request is rejected as "400 BAD REQUEST" by web2py
So for example when calling the restful webservice using ajax call from a local web page,
we get the following error msg in NetBeans log.
Failed to load resource: the server responded with a status of 400
(BAD REQUEST) (10:46:36:182 | error, network) at
127.0.0.1:8000/test/default/api/entries/2.json Failed to load resource: Origin localhost:8383 is not allowed by
Access-Control-Allow-Origin. (10:46:36:183 | error, network) at
127.0.0.1:8000/test/default /api/entries/2.json XMLHttpRequest cannot load 127.0.0.1:8000/test/default /api/entries/2.json. Origin
localhost:8383 is not allowed by Access-Control-Allow-Origin.
(10:46:36:183 | error, javascript) at www/page/test.html
You can add the following line:
response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS"
This is a really old question, but I just managed to solve the exact same problem. In my case the issue was with the controllers; I had to add the following wrapper before any actions:
def CORS(f):
"""
Enables CORS for any action
"""
def wrapper(*args, **kwds):
if request.env.http_origin and request.env.request_method == 'OPTIONS':
response.view = 'generic.json'
return dict()
return f(*args, **kwds)
return wrapper
Then in your controller justy write
#CORS
def whatever():
do_stuff
return dict(stuff)

Flask url_for generating http URL instead of https

I am using url_for to generate a redirect URL when a user has logged out:
return redirect(url_for('.index', _external=True))
However, when I changed the page to a https connection, the url_for still gives me http.
I would like to explicitly ask url_for to add https at the beginning of a URL.
Can you point me how to change it? I looked at Flask docs, without luck.
With Flask 0.10, there will be a much better solution available than wrapping url_for. If you look at https://github.com/mitsuhiko/flask/commit/b5069d07a24a3c3a54fb056aa6f4076a0e7088c7, a _scheme parameter has been added. Which means you can do the following:
url_for('secure_thingy',
_external=True,
_scheme='https',
viewarg1=1, ...)
_scheme sets the URL scheme, generating a URL like https://.. instead of http://. However, by default Flask only generates paths (without host or scheme), so you will need to include the _external=True to go from /secure_thingy to https://example.com/secure_thingy.
However, consider making your website HTTPS-only instead. It seems that you're trying to partially enforce HTTPS for only a few "secure" routes, but you can't ensure that your https-URL is not changed if the page linking to the secure page is not encrypted. This is similar to mixed content.
If you want to affect the URL scheme for all server-generated URLs (url_for and redirect), rather than having to set _scheme on every call, it seems that the "correct" answer is to use WSGI middleware, as in this snippet: http://flask.pocoo.org/snippets/35/
(This Flask bug seems to confirm that that is the preferred way.)
Basically, if your WSGI environment has environ['wsgi.url_scheme'] = 'https', then url_for will generate https: URLs.
I was getting http:// URLs from url_for because my server was deployed behind an Elastic Beanstalk load balancer, which communicates with the server in regular HTTP. My solution (specific to Elastic Beanstalk) was like this (simplified from the snippet linked above):
class ReverseProxied(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
scheme = environ.get('HTTP_X_FORWARDED_PROTO')
if scheme:
environ['wsgi.url_scheme'] = scheme
return self.app(environ, start_response)
app = Flask(__name__)
app.wsgi_app = ReverseProxied(app.wsgi_app)
The Elastic Beanstalk-specific part of that is HTTP_X_FORWARDED_PROTO. Other environments would have other ways of determining whether the external URL included https. If you just want to always use HTTPS, you could unconditionally set environ['wsgi.url_scheme'] = 'https'.
PREFERRED_URL_SCHEME is not the way to do this. It's ignored whenever a request is in progress.
I tried the accepted answer with an url_for arg but I found it easier to use the PREFERRED_URL_SCHEME config variable and set it to https with:
app.config.update(dict(
PREFERRED_URL_SCHEME = 'https'
))
since you don't have to add it to every url_for call.
If your are accessing your website through a reverse proxy like Nginx, then Flask correctly dectects the scheme being HTTP.
Browser -----HTTPS----> Reverse proxy -----HTTP----> Flask
The easiest solution is to configure your reverse proxy to set the X-Forwarded-Proto header. Flask will automatically detect this header and manage scheme accordingly. There is a more detailed explanation in the Flask documentation under the Proxy Setups section. For example, if you use Nginx, you will have to add the following line in your location block.
proxy_set_header X-Forwarded-Proto $scheme;
As other mentionned, if you can't change the configuration of your proxy, you can either use the werkzeug ProxyFix or build your own fix as described in the documentation:
http://flask.pocoo.org/docs/0.12/deploying/wsgi-standalone/#proxy-setups
Setting _scheme on every url_for() call is extremely tedious, and PREFERRED_URL_SCHEME doesn't seem to work. However, mucking with what the request's supposed scheme is at the WSGI level seems to successfully convince Flask to always construct HTTPS URLs:
def _force_https(app):
def wrapper(environ, start_response):
environ['wsgi.url_scheme'] = 'https'
return app(environ, start_response)
return wrapper
app = Flask(...)
app = _force_https(app)
For anyone ending up here recently there is an official uwsgi fixer for this:
https://stackoverflow.com/a/23504684/13777925
FWIW this still didn't work for me since the header wasn't being set correctly so I augmented the ReversedProxied middleware to prefer https if found thusly:
class ReverseProxied(object):
"""
Because we are reverse proxied from an aws load balancer
use environ/config to signal https
since flask ignores preferred_url_scheme in url_for calls
"""
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
# if one of x_forwarded or preferred_url is https, prefer it.
forwarded_scheme = environ.get("HTTP_X_FORWARDED_PROTO", None)
preferred_scheme = app.config.get("PREFERRED_URL_SCHEME", None)
if "https" in [forwarded_scheme, preferred_scheme]:
environ["wsgi.url_scheme"] = "https"
return self.app(environ, start_response)
Called as:
app = flask.Flask(__name__)
app.wsgi_app = ReverseProxied(app.wsgi_app)
This way if you've set the environment var "PREFERRED_URL_SCHEME" explicitly or if the nginx/etc/proxy sets the X_FORWARDED_PROTO, it does the right thing.
I personally could not fix this problem with any of the answers here, but found that simply adding --cert=adhoc to the end of the flask run command, which makes the flask app run with https, solved the issue.
flask run --host=0.0.0.0 --cert=adhoc

How to access wsgi params from a request inside a middleware and a flask request without side effect?

I need to read some values from the wsgi request before my flask app is loaded. If I read the url from the wsgi request I can access the file without any issues once the flask app is loaded (after the middleware runs).
But if I attempt to access the params it seems to remove the post data once the flask app is loaded. I even went to the extreme of wrapping the wsgi request with a special Webob Request to prevent this "read once" problem.
Does anyone know how to access values from the wsgi request in middleware without doing any sort of side effect harm to the request so you can get post data / file data in a flask app?
from webob import Request
class SomeMiddleware(object):
def __init__(self, environ):
self.request = Request(environ)
self.orig_environ = environ
def apply_middleware(self):
print self.request.url #will not do any harm
print self.request.params #will cause me to lose data
Here is my flask view
#app.route('/')
def hello_world():
from flask import request
the_file = request.files['file']
print "and the file is", the_file
From what I can tell, this is a limitation of the way that WSGI works. The stream needs only be consumable once (PEP 333 and 3333 only require that the stream support read* calls, tell does not need to be supported). Once the stream is exhausted it cannot be re-streamed to other WSGI applications further "inward". Take a look at these two sections of Werkzeug's documentation for more information:
http://werkzeug.pocoo.org/docs/request_data/
http://werkzeug.pocoo.org/docs/http/#module-werkzeug.formparser
The way to avoid this issue is to wrap the input stream (wsgi.input) in an object that implements the read and readline methods. Then, only when the final application in the chain actually attempts to exhaust the stream will your methods be run. See Flask's documentation on generating a request checksum for an example of this pattern.
That being said, are you sure a middleware is the best solution to your problems? If you need to perform some action (dispatch, logging, authentication) based on the content of the body of the request you may be better off making it a part of your application, rather than a stand-alone application of its own.

Categories

Resources