Igors
Igors

Reputation: 121

Google App Engine flask SSL and OAuth2 problems

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 fine
Any 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.

Upvotes: 6

Views: 2127

Answers (2)

Transcendence
Transcendence

Reputation: 2696

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

Upvotes: 2

Kevin M
Kevin M

Reputation: 43

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.

Upvotes: 3

Related Questions