[英]Google+ login - Server side flow - Python - Google App Engine
I am building an app on Google App Engine using Flask. 我正在使用Flask在Google App Engine上构建应用程序。 I am implementing Google+ login from the server-side flow described in https://developers.google.com/+/web/signin/server-side-flow . 我正在从https://developers.google.com/+/web/signin/server-side-flow中所述的服务器端流程实现Google+登录。 Before switching to App Engine, I had a very similar flow working. 切换到App Engine之前,我的工作流程非常相似。 Perhaps I have introduced an error since then. 从那时起,也许我引入了一个错误。 Or maybe it is an issue with my implementation in App Engine. 也许这与我在App Engine中的实现有关。
I believe the url redirected to by the Google login flow should have a GET argument set "gplus_id", however, I am not receiving this parameter. 我相信Google登录流程重定向到的网址应具有GET参数集“ gplus_id”,但是,我没有收到此参数。
I have a login button created by: 我有一个通过以下方式创建的登录按钮:
(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'
});
}
In the javascript code for the page I have a function to initiate the flow: 在页面的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);
}
This initiates the flow at "/connect" (See step 8. referenced in the above doc ): 这将在“ / 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
...
However, the flow stops at if result['user_id'] != gplus_id:
, saying "Token's user ID doesn't match given user ID.". 但是,流程将在if result['user_id'] != gplus_id:
处停止,说“令牌的用户ID与给定的用户ID不匹配”。 result['user_id']
is a valid users ID, but gplus_id
is None. result['user_id']
是有效的用户ID,但gplus_id
为“无”。
The line gplus_id = request.args.get('gplus_id')
is expecting the GET args to contain 'gplus_id', but they only contain 'state'. gplus_id = request.args.get('gplus_id')
期望GET args包含'gplus_id',但它们仅包含'state'。 Is this a problem with my javascript connectServer function? 我的javascript connectServer函数是否有问题? Should I include 'gplus_id' there? 我应该在其中添加“ gplus_id”吗? Surely I don't know it at that point. 当然我当时还不知道。 Or something else? 或者是其他东西?
Similar to this question , I believe this is an issue with incomplete / not up to date / inconsistent documentation. 与此问题类似,我认为这是文档不完整/不是最新/不一致的问题。
Where https://developers.google.com/+/web/signin/server-side-flow suggests that gplus_id
will be returned in the GET arguments, this is not the case for the flow I was using. https://developers.google.com/+/web/signin/server-side-flow建议在GET参数中返回gplus_id
,但我使用的流程并非如此。
I found my answer in https://github.com/googleplus/gplus-quickstart-python/blob/master/signin.py , which includes this snippet: 我在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']
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.