I am trying to set a cookie with Flask after login and redirect on the front end in Javascript.
#app.route("/login")
#auth.login_required
def get_auth_token():
token = g.user.generate_auth_token()
request = make_response()
token = str(token.decode("ascii"))
request.set_cookie("token", value = token)
return request, 200
No matter if I have the redirect in or not, the cookie never sets. I've tried commenting out my redirect on the front end, I've tried setting my cookie with secure = false but none of that seems to work. What am I missing? If needed, I can provide the generate_suth_token function, but I know that is working properly. I am serving on localhost:5000 and using Flask 0.12.2, and received no cookie warnings in the server log.
If Flask service and client service are being hosted on different domains (e. g Flask uses 127.0.0.1:8080 and a client uses 127.0.0.1:3000) in this case, cookies should be set with domain parameter otherwise they will not be available.
resp.set_cookie('cookie_key', value="cookie_value", domain='127.0.0.1')
Find more info about domain parameter here
Related
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.
I'm trying to put some security on my Flask web app. As a first step I'm going to make my session cookie secure by setting SESSION_COOKIE_SECURE to true.
But after I get my session cookie from "inspect element" I can decode session cookie easily and there is no difference whether I add SESSION_COOKIE_SECURE or not.
Here is my code:
from flask import Flask, request, app, render_template, session, send_file, redirect
MyApp = Flask(__name__)
MyApp.secret_key = "something"
application = MyApp
if __name__ == "__main__":
MyApp.debug = False
MyApp.config.update(
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax',
)
MyApp.config["SESSION_PERMANENT"] = True
MyApp.run()
I also tried to add this attribute using the following syntax but this made no difference:
MyApp.config['SESSION_COOKIE_SECURE'] = True
When I try to print SESSION_COOKIE_SECURE I get this error
Traceback (most recent call last):
File "...", line ..., in <module>
print(MyApp.session_cookie_secure)
AttributeError: 'Flask' object has no attribute 'session_cookie_secure'
My Flask version is 1.0.2, and I'm on HTTPS.
Setting SESSION_COOKIE_SECURE does not encrypt the cookie value, no. When set, this causes Flask to create cookies with the "Secure" flag set. This means that a browser can only return the cookie to the server over an encrypted connection, nothing more. The setting doesn't change anything about the cookie value itself.
Flask produces cookies that are cryptographically signed, by default. That means that the cookie contents can be decoded but not altered, because a third party without access to the server secret can't create a valid signature for the cookie.
You generally don't need to encrypt your session cookie if you a) use HTTPS (which encrypts the data from outsiders) and b) protect your web app from XSS attacks. Without an XSS attack vector, attackers can't get access to your cookie contents at all anyway.
You certainly don't need to do so here, as SESSION_COOKIE_HTTPONLY means that the browser will never expose the cookie to JavaScript, and only someone with full access to the browser can see the cookie value.
Flask doesn't have a 'encrypt cookie' setting, because it is not deemed necessary when you can secure the cookie in other ways. You should not store information in a session cookie so sensitive that it should be protected from the end-user with access to the browser storage; keep such data on the server and only store a unique identifier in the session to retrieve that secret data later on.
If for some reason you can't keep such secrets out of the session cookie and are unwilling to accept that the end-user can read this data, then you'll have to encrypt the cookie yourself or use an alternative session provider for Flask, such as EncryptedSession.
As for the attribute error: only a few configuration settings are accessible as attributes on the Flask object. To print arbitrary configuration settings, use the app.config object:
print(MyApp.config['SESSION_COOKIE_SECURE'])
i'm trying to create flask web application on google app engine python 3 flexible env with oauth2 authentication on custom domain.
So, the problems are following :
1)I have added custom domain to my project, and also added SSL to that custom domain. In google cloud console everything seems fine, but SSL not showing/working on my custom domain.
Maybe problem is in my dispatch file?
dispatch:
- url: 'mycustomdomain.com/'
service: default
- url: 'www.mycustomdomain.com/'
service: default
2)I can't login in despite having SSL on https://[project-id].appspot.com. After pressing "Login with Google" i'm redirecting to /authorize, where i choose account from which i want to login. After that happens redirect to /oauth2callback, https mystically changes to http and i can't login, getting following error InsecureTransportError: (insecure_transport) OAuth 2 MUST utilize https.
Python authorize :
#app.route('/authorize')
def authorize():
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)
authorization_url, state = flow.authorization_url(
access_type='offline',
include_granted_scopes='true')
flask.session['state'] = state
return flask.redirect(authorization_url)
Python oauth2callback:
#app.route('/oauth2callback')
def oauth2callback():
state = flask.session['state']
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)
authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)
credentials = flow.credentials
flask.session['credentials'] = credentials_to_dict(credentials)
session = flow.authorized_session()
flask.session['username_output'] = session.get(
'https://www.googleapis.com/userinfo/v2/me').json()
return flask.redirect(flask.url_for('map'))
When testing locally i'm using os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' and locally it is working perfectly fineAny suggestions??
SOLUTION
Problem solved -
1)i forgot to add SSL certificate to domain(where domain is hosted), thats why SSL wouldn't show.. I know, so lame mistake.
2)First, I forced SSL with SSLify for flask, but that didn't solve the problem, which was in following line
authorization_response = flask.request.url
no matter what, this line of code gave me http://, i tried to change every scheme that i could find to https in flask.url_for(_scheme='https'), but that didn't help either, so for now, my workaround is
authorization_response = authorization_response.replace('http', 'https')
i know, not the best solution, but it works.
I ran into this issue as well and found out that the issue stems from authorization_response = flask.request.url because that URL defaults to HTTP rather than HTTPS.
My hacky solution was to run a quick regex substitution and force the URL to be HTTPS:
import re
....
authorization_response = request.url
new_auth = re.sub(
"http:",
"https:",
authorization_response
)
flow.fetch_new_token(new_auth)
There are certainly better ways to accomplish this, but it works.
I ran into this error because I was using nginx in front of gunicorn/flask.
I fixed it by adding
proxy_set_header X-Forwarded-Proto $scheme;
to my nginx config, and then adding
from werkzeug.middleware.proxy_fix import ProxyFix
application.wsgi_app = ProxyFix(application.wsgi_app, x_proto=1)
to the filing being called by gunicorn.
For your specific problem on the GAE Python 3 Flex environment, you'll need to configure gunicorn to do the same thing.
App Engine terminates the HTTPS connection at the load balancer and forwards the request to your application. Most applications do not need to know if the request was sent over HTTPS or not, but applications that do need this information should configure Gunicorn to trust the App Engine proxy in their gunicorn.conf.py:
forwarded_allow_ips = '*'
secure_scheme_headers = {'X-Forwarded-Proto': 'https'}
Gunicorn will now ensure that the wsgi.url_scheme to 'https', which most web frameworks will use as indication of the request is secure. If your WSGI server or framework doesn't support this, just check the value of the X-Forwarded-Proto header manually.
https://cloud.google.com/appengine/docs/flexible/python/runtime#recommended_gunicorn_configuration
I've recently added a SSL certificate to my webapp. It's deployed on Amazon Web Services uses load balancers. The load balancers work as reverse proxies, handling external HTTPS and sending internal HTTP. So all traffic to my Flask app is HTTP, not HTTPS, despite being a secure connection.
Because the site was already online before the HTTPS migration, I used SSLify to send 301 PERMANENT REDIRECTS to HTTP connections. It works despite all connections being HTTP because the reverse proxy sets the X-Forwarded-Proto request header with the original protocol.
The problem
url_for doesn't care about X-Forwarded-Proto. It will use the my_flask_app.config['PREFERRED_URL_SCHEME'] when a scheme isn't available, but during a request a scheme is available. The HTTP scheme of the connection with the reverse proxy.
So when someone connects to https://example.com, it connects to the load balancer, which then connects to Flask using http://example.com. Flask sees the http and assumes the scheme is HTTP, not HTTPS as it originally was.
That isn't a problem in most url_for used in templates, but any url_for with _external=True will use http instead of https. Personally, I use _external=True for rel=canonical since I heard it was recommended practice. Besides that, using Flask.redirect will prepend non-_external urls with http://example.com, since the redirect header must be a fully qualified URL.
If you redirect on a form post for example, this is what would happen.
Client posts https://example.com/form
Server issues a 303 SEE OTHER to http://example.com/form-posted
SSLify then issues a 301 PERMANENT REDIRECT to https://example.com/form-posted
Every redirect becomes 2 redirects because of SSLify.
Attempted solutions
Adding PREFERRED_URL_SCHEME config
https://stackoverflow.com/a/26636880/1660459
my_flask_app.config['PREFERRED_URL_SCHEME'] = 'https'
Doesn't work because there is a scheme during a request, and that one is used instead. See https://github.com/mitsuhiko/flask/issues/1129#issuecomment-51759359
Wrapping a middleware to mock HTTPS
https://stackoverflow.com/a/28247577/1660459
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)
As is, this didn't work because I needed that app later. So I used wsgi_app instead.
def _force_https(wsgi_app):
def wrapper(environ, start_response):
environ['wsgi.url_scheme'] = 'https'
return wsgi_app(environ, start_response)
return wrapper
app = Flask(...)
app.wsgi_app = _force_https(app.wsgi_app)
Because wsgi_app is called before any app.before_request handlers, doing this makes SSLify think the app is already behind a secure request and then it won't do any HTTP-to-HTTPS redirects.
Patching url_for
(I can't even find where I got this one from)
from functools import partial
import Flask
Flask.url_for = partial(Flask.url_for, _scheme='https')
This could work, but Flask will give an error if you set _scheme but not _external. Since most of my app url_for are internal, it doesn't work at all.
I was having these same issues with `redirect(url_for('URL'))' behind an AWS Elastic Load Balancer recently & I solved it this using the werkzeug.contrib.fixers.ProxyFix
call in my code.
example:
from werkzeug.contrib.fixers import ProxyFix
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)
The ProxyFix(app.wsgi_app) adds HTTP proxy support to an application that was not designed with HTTP proxies in mind. It sets REMOTE_ADDR, HTTP_HOST from X-Forwarded headers.
Example:
from werkzeug.middleware.proxy_fix import ProxyFix
# App is behind one proxy that sets the -For and -Host headers.
app = ProxyFix(app, x_for=1, x_host=1)
Digging around Flask source code, I found out that url_for uses the Flask._request_ctx_stack.top.url_adapter when there is a request context.
The url_adapter.scheme defines the scheme used. To make the _scheme parameter work, url_for will swap the url_adapter.scheme temporarily and then set it back before the function returns.
(this behavior has been discussed on github as to whether it should be the previous value or PREFERRED_URL_SCHEME)
Basically, what I did was set url_adapter.scheme to https with a before_request handler. This way doesn't mess with the request itself, only with the thing generating the urls.
def _force_https():
# my local dev is set on debug, but on AWS it's not (obviously)
# I don't need HTTPS on local, change this to whatever condition you want.
if not app.debug:
from flask import _request_ctx_stack
if _request_ctx_stack is not None:
reqctx = _request_ctx_stack.top
reqctx.url_adapter.url_scheme = 'https'
app.before_request(_force_https)
I've got a Python Flask app running on Heroku (Cedar stack) with two custom domains (one with and one without the www subdomain). I'd like to redirect all incoming requests to the www. version of the resource requested (the inverse of this question). I think I need some WSGI middleware for this but I can't find a good example.
How do I do this?
An easier solution than to create a separate Heroku app would be a before_request function.
from urllib.parse import urlparse, urlunparse
#app.before_request
def redirect_nonwww():
"""Redirect non-www requests to www."""
urlparts = urlparse(request.url)
if urlparts.netloc == 'example.com':
urlparts_list = list(urlparts)
urlparts_list[1] = 'www.example.com'
return redirect(urlunparse(urlparts_list), code=301)
This will redirect all non-www requests to www using a "HTTP 301 Moved Permanently" response.
According to the Heroku Docs, you've got the right idea about using the www subdomain (eg www.foo.com) vs apex domain (eg foo.com). Their suggestion for dealing with this is to use a DNS layer redirect:
To quote:
Subdomain redirection
Subdomain redirection results in a 301 permanent redirect to the
specified subdomain for all requests to the apex domain so all current
and future requests are properly routed and the full www hostname is
displayed in the user’s location field.
Almost all DNS providers offer domain redirection services - sometimes
also called domain forwarding. DNSimple provides a convenient URL
redirect seen here redirecting from the heroku-sslendpoint.com apex
domain to the www.heroku-sslendpoint.com subdomain.
Source:
http://devcenter.heroku.com/articles/avoiding-apex-domains-dns-arecords#subdomain_redirection
Hope that helps!
One possible approach would be to add a function to listen on request_started, and do the appropriate redirection.
This signal is sent before any request processing started but when the
request context was set up. Because the request context is already
bound, the subscriber can access the request with the standard global
proxies such as request.
What I ended up doing was creating a second Heroku app, assigning the non-www hostname to that one and using a catch all Flask route to redirect to the www version keeping the path intact.
Try this:
#app.route('/', methods=['GET', 'POST'], subdomain="www")
#app.route('/<string:path>', methods=['GET', 'POST'], subdomain="www")
def www(path=''):
return redirect(url_for('app.index')+path)