2014-04-07 10 views
5

Ich baue eine App auf Google App Engine mit Flask. Ich implementiere die Google+ Anmeldung über den serverseitigen Ablauf, der in https://developers.google.com/+/web/signin/server-side-flow beschrieben ist. Bevor ich zu App Engine wechselte, hatte ich einen sehr ähnlichen Ablauf. Vielleicht habe ich seitdem einen Fehler eingeführt. Oder vielleicht ist es ein Problem mit meiner Implementierung in App Engine.Google+ Anmeldung - Server Side Flow - Python - Google App Engine

Ich glaube, die URL, umgeleitet durch den Google-Login-Flow, sollte ein GET-Argument "gplus_id" haben, aber ich empfange diesen Parameter nicht.

Ich habe eine Login-Button erstellt von:

(function() { 
    var po = document.createElement('script'); 
    po.type = 'text/javascript'; po.async = true; 
    po.src = 'https://plus.google.com/js/client:plusone.js?onload=render'; 
    var s = document.getElementsByTagName('script')[0]; 
    s.parentNode.insertBefore(po, s); 
})(); 

function render() { 
    gapi.signin.render('gplusBtn', { 
    'callback': 'onSignInCallback', 
    'clientid': '{{ CLIENT_ID }}', 
    'cookiepolicy': 'single_host_origin', 
    'requestvisibleactions': 'http://schemas.google.com/AddActivity', 
    'scope': 'https://www.googleapis.com/auth/plus.login', 
    'accesstype': 'offline', 
    'width': 'iconOnly' 
    }); 
} 

im JavaScript-Code für die Seite, die ich eine Funktion haben, um den Fluss zu initiieren:

var helper = (function() { 
    var authResult = undefined; 

    return { 
    onSignInCallback: function(authResult) { 
     if (authResult['access_token']) { 
     // The user is signed in 
     this.authResult = authResult; 
     helper.connectServer(); 
     } else if (authResult['error']) { 
     // There was an error, which means the user is not signed in. 
     // As an example, you can troubleshoot by writing to the console: 
     console.log('GPlus: There was an error: ' + authResult['error']); 
     } 
     console.log('authResult', authResult); 
    }, 
    connectServer: function() { 
     $.ajax({ 
     type: 'POST', 
     url: window.location.protocol + '//' + window.location.host + '/connect?state={{ STATE }}', 
     contentType: 'application/octet-stream; charset=utf-8', 
     success: function(result) { 
      // After we load the Google+ API, send login data. 
      gapi.client.load('plus','v1',helper.otherLogin); 
     }, 
     processData: false, 
     data: this.authResult.code, 
     error: function(e) { 
      console.log("connectServer: error: ", e); 
     } 
     }); 
    } 
    } 
})(); 

/** 
* Calls the helper method that handles the authentication flow. 
* 
* @param {Object} authResult An Object which contains the access token and 
* other authentication information. 
*/ 
function onSignInCallback(authResult) { 
    helper.onSignInCallback(authResult); 
} 

Dieser den Fluss beginnt an „/ connect "(Siehe Schritt 8 in the above doc verwiesen):

@app.route('/connect', methods=['GET', 'POST']) 
def connect(): 
    # Ensure that this is no request forgery going on, and that the user 
    # sending us this connect request is the user that was supposed to. 
    if request.args.get('state', '') != session.get('state', ''): 
     response = make_response(json.dumps('Invalid state parameter.'), 401) 
     response.headers['Content-Type'] = 'application/json' 
     return response 

    # Normally the state would be a one-time use token, however in our 
    # simple case, we want a user to be able to connect and disconnect 
    # without reloading the page. Thus, for demonstration, we don't 
    # implement this best practice. 
    session.pop('state') 

    gplus_id = request.args.get('gplus_id') 
    code = request.data 

    try: 
     # Upgrade the authorization code into a credentials object 
     oauth_flow = client.flow_from_clientsecrets('client_secrets.json', scope='') 
     oauth_flow.redirect_uri = 'postmessage' 
     credentials = oauth_flow.step2_exchange(code) 
    except client.FlowExchangeError: 
     app.logger.debug("connect: Failed to upgrade the authorization code") 
     response = make_response(
      json.dumps('Failed to upgrade the authorization code.'), 401) 
     response.headers['Content-Type'] = 'application/json' 
     return response 

    # Check that the access token is valid. 
    access_token = credentials.access_token 
    url = ('https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=%s' 
      % access_token) 
    h = httplib2.Http() 
    result = json.loads(h.request(url, 'GET')[1]) 
    # If there was an error in the access token info, abort. 
    if result.get('error') is not None: 
     response = make_response(json.dumps(result.get('error')), 500) 
     response.headers['Content-Type'] = 'application/json' 
     return response 
    # Verify that the access token is used for the intended user. 
    if result['user_id'] != gplus_id: 
     response = make_response(
      json.dumps("Token's user ID doesn't match given user ID."), 401) 
     response.headers['Content-Type'] = 'application/json' 
     return response 
    ... 

jedoch die Strömung stoppt bei if result['user_id'] != gplus_id:, und sagen, dass die Benutzer-ID des Tokens nicht mit der angegebenen Benutzer-ID übereinstimmt. result['user_id'] ist eine gültige Benutzer-ID, aber gplus_id ist Keine.

Die Zeile gplus_id = request.args.get('gplus_id') erwartet, dass die GET-Argumente 'gplus_id' enthalten, aber sie enthalten nur 'state'. Ist das ein Problem mit meiner javascript connectServer Funktion? Sollte ich "gplus_id" dort einschließen? Sicher weiß ich es zu diesem Zeitpunkt nicht. Oder etwas anderes?

Antwort

6

Ähnlich wie this question, ich glaube, das ist ein Problem mit unvollständig/nicht aktuell/inkonsistent Dokumentation.

Wo https://developers.google.com/+/web/signin/server-side-flow schlägt vor, dass gplus_id in den GET-Argumenten zurückgegeben wird, ist dies nicht der Fall für den Fluss, den ich verwendete.

fand ich meine Antwort in https://github.com/googleplus/gplus-quickstart-python/blob/master/signin.py, die diesen Schnipsel enthält:

# An ID Token is a cryptographically-signed JSON object encoded in base 64. 
# Normally, it is critical that you validate an ID Token before you use it, 
# but since you are communicating directly with Google over an 
# intermediary-free HTTPS channel and using your Client Secret to 
# authenticate yourself to Google, you can be confident that the token you 
# receive really comes from Google and is valid. If your server passes the 
# ID Token to other components of your app, it is extremely important that 
# the other components validate the token before using it. 
gplus_id = credentials.id_token['sub'] 
+0

am Nachmittag mit diesem Problem verloren. Ich habe die Lösung den Google-Entwicklern vorgelegt. Wenn Sie es auch tun können, würde vielleicht anderen helfen, nicht so viel Zeit zu verlieren. Vielen Dank! – marcelocra

+1

Es gibt zu viele Lücken in der Dokumentation zu Google+ Sign-in und zur tatsächlichen Implementierung. – Shekhar