pythongoogle-app-engineflaskoauthflask-dance

How to implement social account authentication using http protocol


I'am trying to implement Google social login locally using Flask, Google App Engine and Flask-dance.

I followed the example provided by Flask-dance author from this link.

This is the main file:

from flask import Flask, url_for, redirect
from flask_dance.contrib.google import make_google_blueprint, google
from flask_dance.consumer import oauth_authorized, oauth_error
from werkzeug.contrib.fixers import ProxyFix

app = Flask('application')
app.wsgi_app = ProxyFix(app.wsgi_app)

# You must configure these 3 values from Google APIs console
# https://code.google.com/apis/console
GOOGLE_CLIENT_ID = 'my-client-id'
GOOGLE_CLIENT_SECRET = 'my-client-secret'

app.config["GOOGLE_OAUTH_CLIENT_ID"] = GOOGLE_CLIENT_ID
app.config["GOOGLE_OAUTH_CLIENT_SECRET"] = GOOGLE_CLIENT_SECRET
google_bp = make_google_blueprint(
    client_id=app.config['GOOGLE_OAUTH_CLIENT_ID'],
    client_secret=app.config['GOOGLE_OAUTH_CLIENT_SECRET'],
    redirect_to="index_man_2",
    scope=["https://www.googleapis.com/auth/userinfo.profile",
    "https://www.googleapis.com/auth/userinfo.email"]
)

app.register_blueprint(google_bp, url_prefix="/login")


@app.route("/login-gmail")
def index_gmail():
    if not google.authorized:
        return redirect(url_for("google.login"))
    resp = google.get("/oauth2/v1/userinfo")
    assert resp.ok, resp.text
    return "ok"

When i access to http://localhost:8080/login-gmail, the server redirect me to choose google account page. Then, when i choose an account, i get this error:

INFO 2019-07-11 14:47:13,476 module.py:861] default: "GET /login/google HTTP/1.1" 302 989 WARNING 2019-07-11 14:47:21,345 urlfetch_stub.py:575] Stripped prohibited headers from URLFetch request: ['Content-Length'] WARNING 2019-07-11 13:47:21,828 connectionpool.py:403] Failed to parse headers (url=https://accounts.google.com:443/o/oauth2/token): expected httplib.Message, got . Traceback (most recent call last): File "C:\Users\tah\Documents\some-name\m\src\lib\urllib3\connectionpool.py", line 399, in _make_request assert_header_parsing(httplib_response.msg) File "C:\Users\Tah\Documents\some-name\m\src\lib\urllib3\util\response.py", line 56, in assert_header_parsing type(headers))) TypeError: expected httplib.Message, got . error message : EXCEPTION IN (1982, ('Connection broken: IncompleteRead(35 bytes read)', IncompleteRead(35 bytes read))) ('Connection broken: IncompleteRead(35 bytes read)', IncompleteRead(35 bytes read)) INFO 2019-07-11 13:47:21,884 recording.py:676] Saved; key: appstats:041000, part: 455 bytes, full: 18063 bytes, overhead: 0.000 + 0.012; link: http://localhost:8080/_ah/stats/details?time=1562852841009 INFO
2019-07-11 14:47:21,895 module.py:861] default: "GET /login/google/authorized?state=sfUHmqfKiy61fnvh1UUsVydJv3vO5L&code=4%2FgwHWN8roL2HIxqxtBoFKySXod_jErJ0NB7ofNpdFtLwS2Zebc2rx959sPDOvUThrdlKfQEKWAj0bEbtJxBsskao&scope=email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+openid&authuser=2&session_state=7ea8a7963e2773849220b0eb3ddf063f9c5e3ef8..3331&prompt=consent HTTP/1.1" 500 41241

From this answer, i understand that If Flask-Dance is generating a redirect URL using HTTP, that means that Flask believes that the incoming request is using HTTP. If the incoming request is actually using HTTPS, then Flask is getting confused somewhere, mostly like due to a proxy. But this answer did not tell us how to fix the error.

Thank you for your answers.


Solution

  • I assume that you have defined this environment variables in app.yaml:

    env_variables:
      OAUTH_INSECURE_TRANSPORT: '1'
      OAUTHLIB_INSECURE_TRANSPORT: '1'
    

    To fix your issue, you need to use requests toolbelt. The toolbelt comes with several different transport adapters for you to use with requests.

    First, you need to install the library:

    pip install -t lib requests-toolbelt
    

    Then you need to add this lines to main file:

    import requests_toolbelt.adapters.appengine
    # Use the App Engine Requests adapter. This makes sure that Requests uses
    # URLFetch.
    requests_toolbelt.adapters.appengine.monkeypatch()
    

    So, the main file will be similar to this:

    from flask import Flask, url_for, redirect
    from flask_dance.contrib.google import make_google_blueprint, google
    from flask_dance.consumer import oauth_authorized, oauth_error
    import requests_toolbelt.adapters.appengine
    
    app = Flask('application')
    # Use the App Engine Requests adapter. This makes sure that Requests uses
    # URLFetch.
    requests_toolbelt.adapters.appengine.monkeypatch()
    
    # You must configure these 3 values from Google APIs console
    # https://code.google.com/apis/console
    GOOGLE_CLIENT_ID = 'my-client-id'
    GOOGLE_CLIENT_SECRET = 'my-client-secret'
    #...