Вход в Google+ – поток на стороне сервера – Python – Google App Engine

Я создаю приложение в Google App Engine с помощью Flask. Я использую логин Google+ из потока на стороне сервера, описанного в https://developers.google.com/+/web/signin/server-side-flow . Перед переключением на App Engine у ​​меня был очень похожий поток. Возможно, с тех пор я представил ошибку. Или, может быть, это проблема с моей реализацией в App Engine.

Я считаю, что URL-адрес, перенаправленный потоком входа в Google, должен иметь параметр GET, заданный «gplus_id», однако я не получаю этот параметр.

У меня есть кнопка входа в систему, созданная:

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

В javascript-коде для страницы у меня есть функция, чтобы инициировать поток:

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

Это инициирует поток при «/ connect» (см. Шаг 8, указанный в приведенном выше документе ):

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

Однако поток останавливается, if result['user_id'] != gplus_id: «Идентификатор пользователя Token не соответствует указанному идентификатору пользователя». result['user_id'] является допустимым идентификатором пользователя, но gplus_id – None.

Строка gplus_id = request.args.get('gplus_id') ожидает, что аргументы GET содержат «gplus_id», но они содержат только «состояние». Это проблема с моей функцией javascript connectServer? Должен ли я включать 'gplus_id' там? Конечно, я не знаю этого в этот момент. Или что-то другое?

Подобно этому вопросу , я считаю, что это проблема с неполной / не обновленной / несогласованной документацией.

Где https://developers.google.com/+/web/signin/server-side-flow предполагает, что gplus_id будет возвращен в аргументах GET, это не относится к потоку, который я использовал.

Я нашел свой ответ в https://github.com/googleplus/gplus-quickstart-python/blob/master/signin.py , который включает в себя этот фрагмент:

 # 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']