2014-04-07 20 views
5

Sto costruendo un'app su Google App Engine utilizzando Flask. Sto implementando l'accesso a Google+ dal flusso sul lato server descritto in https://developers.google.com/+/web/signin/server-side-flow. Prima di passare ad App Engine, ho avuto un flusso molto simile. Forse ho introdotto un errore da allora. O forse è un problema con la mia implementazione in App Engine.Accesso Google+ - Flusso lato server - Python - Google App Engine

Credo che l'URL reindirizzato dal flusso di accesso di Google dovrebbe avere un argomento GET impostato "gplus_id", tuttavia, non sto ricevendo questo parametro.

Ho un pulsante di accesso creato da:

(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' 
    }); 
} 

Nel codice javascript per la pagina Ho una funzione di avviare il flusso:

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); 
} 

Questo avvia il flusso a "/ connect "(Vedi punto 8. riferimento in the above doc):

@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 
    ... 

Tuttavia, il flusso si arresta a if result['user_id'] != gplus_id:, dicendo "L'ID utente del token non corrisponde all'ID utente fornito". result['user_id'] è un ID utente valido, ma gplus_id è Nessuno.

La riga gplus_id = request.args.get('gplus_id') si aspetta che gli argomenti GET contengano "gplus_id", ma contengono solo "stato". Si tratta di un problema con la mia funzione javascript connectServer? Dovrei includere 'gplus_id' lì? Sicuramente non lo so a quel punto. O qualcos'altro?

risposta

6

Simile a this question, credo che questo sia un problema con documentazione incompleta/non aggiornata/incoerente.

Dove https://developers.google.com/+/web/signin/server-side-flow suggerisce che gplus_id verrà restituito negli argomenti GET, questo non è il caso per il flusso che stavo usando.

ho trovato la mia risposta in https://github.com/googleplus/gplus-quickstart-python/blob/master/signin.py, che comprende questo frammento:

# 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

perso il pomeriggio con questo problema. Ho inviato la soluzione agli sviluppatori di Google. Se riesci a farlo anche tu potresti aiutare gli altri a non perdere molto tempo. Grazie! – marcelocra

+1

Ci sono troppe lacune nella documentazione relativa all'accesso di Google+ e nell'attuazione effettiva. – Shekhar