我正在开发用Python编写的Google App Engine应用程序,并使用Endpoints API。同时,我正在编写一个Chrome扩展来与Endpoints API交互。我在Endpoints和授权方面遇到了很多问题。目前,我的设置如下:
端点API (Python)
from google.appengine.ext import endpoints
from protorpc import message_types
from protorpc import remote
ALLOWED_CLIENT_IDS = ['client_id_from_google_api_console',
endpoints.API_EXPLORER_CLIENT_ID]
@endpoints.api(name='my_api',version='v1', description='My API',
allowed_client_ids=ALLOWED_CLIENT_IDS)
class MyApi(remote.Service):
@endpoints.method(message_types.VoidMessage, DeviceListResponse,
name='user.device.list', path='user/device/list',
http_method='GET')
def user_device_list(self, request):
current_user = endpoints.get_current_user()
if current_user is None:
raise endpoints.UnauthorizedException('You must authenticate first.')
if current_user.user_id() is None:
raise endpoints.NotFoundException("Your user id was not found.")
return DeviceListResponse(devices=[]) #Hypothetically return devices
api_service = endpoints.api_server([MyApi], restricted=False)Google API控制台
JS源码包括: chrome-extensions://chrome_app_id
CHROME扩展(JS)
var apiRoot = "https://my_app_id.appspot.com/_ah/api";
var clientID = "client_id_from_google_api_console";
var oauthScopes = ["https://www.googleapis.com/auth/userinfo.email"];
var responseType = "token id_token";
//Helper method to log to the console
function l(o){console.log(o);}
function oauthSignin(mode) {
gapi.auth.authorize({client_id: clientID, scope: oauthScopes,
immediate: mode, response_type: responseType}, function() {
var request = gapi.client.oauth2.userinfo.get();
request.execute(function(resp) {
authenticated = !resp.code;
if(authenticated) {
var token = gapi.auth.getToken();
token.access_token = token.id_token;
gapi.auth.setToken(token);
l("Successfully authenticated. Loading device list");
gapi.client.my_api.user.device.list({}).execute(function(resp) {
if(resp.code) {
l("Response from device list: " + resp.message);
}
l(resp);
});
}
});
});
}
//This get's called when the page and js library has loaded.
function jsClientLoad() {
l("JS Client Libary loaded. Now loading my_api and oauth2 APIs.");
var apisToLoad;
var callback = function() {
if (--apisToLoad == 0) {
l("APIs have loaded.")
oauthSignin(true);
} else {
l("Waiting for " + apisToLoad + " API" + (apisToLoad>1?"s":"") + " to load.");
}
}
apisToLoad = 2; // must match number of calls to gapi.client.load()
gapi.client.load('my_api', 'v1', callback, apiRoot);
gapi.client.load('oauth2', 'v2', callback);
}结果
现在我已经展示了代码的主要部分(注意,我必须在不上传整个代码的情况下对其进行一些修改),如果我转到Google API Explorer并运行该方法,我会得到一个200响应。如果我在Chrome扩展中运行它,我会得到一个404代码,消息是“你的用户id没有找到”。
发布于 2013-05-21 11:43:39
目前还不清楚为什么/何时会产生200;它不应该出现。正如在Function User.getUserId() in Cloud endpoint api returns null for a user object that is not null中提到的,这是一个known issue。
TLDR;
在从endpoints.get_current_user()返回的结果中,user_id将永远不会被填充。存在一种解决方法:通过将用户对象存储在数据存储中,然后检索它(如果使用ndb,则使用新的get ),将填充user_id()值。
您应该强烈考虑使用与帐户关联的Google配置文件ID,而不是App Engine用户ID。
历史/解释:
endpoints的目的是同时使用持有者令牌和ID令牌(适用于安卓)。ID令牌是一种特殊类型的JWT (JSON web令牌),与设备上的加密一起签名。因此,从这些令牌解析用户只能确定该令牌中编码的信息(有关这方面的更多信息,请参阅Cloud endpoints oauth2 error )。
由于这些令牌是由App Engine外部的通用Google Auth提供者(OAuth 2.0)创建的,因此App Engine用户ID不为该服务所知/共享。因此,当使用ID令牌对请求进行签名时,不可能填充user_id()。
当使用标准的持有者令牌(这对于您的Chrome应用程序来说很好)时,将使用App Engine OAuth API。当OAuth API调用
oauth.get_current_user(some_scope)(其中oauth是google.appengine.api.oauth),
oauth.oauth_api._maybe_call_get_oauth_user(_scope=None)调用method。这将创建一个指向共享App Engine层的RPC,该层提供一个能够从令牌中获取当前用户的服务。在这种情况下,将设置返回的用户的user_id(),但是,不会为endpoints.get_current_user保留用户值,只保留电子邮件和身份验证域。
另一种解决方法:
只有当oauth.get_current_user()调用生成RPC时,它才是开销很大的。_maybe_call_get_oauth_user方法存储上一次调用的值,因此第二次调用oauth.get_current_user()不会产生网络/速度开销,只会花费几纳秒的时间从Python dict查找值。
这一点至关重要,因为endpoints.get_current_user()使用对oauth.get_current_user()的调用来确定持有者令牌用户,所以如果您想再次调用它,您会担心性能问题。
如果您知道自己永远不会使用ID令牌,或者可以很容易地确定这些情况,那么您可以将代码更改为只调用这两者:
endpoints_user = endpoints.get_current_user()
if endpoints_user is None:
raise endpoints.UnauthorizedException(...)
oauth_user = oauth.get_current_user(known_scope)
if oauth_user is None or oauth_user.user_id() is None:
# This should never happen
raise endpoints.NotFoundException(...)注意::我们仍然必须调用endpoints.get_current_user(),因为它总是确保我们的令牌只为我们所允许的一个特定作用域和白名单中的一个特定客户端ID创建,以便与我们的应用程序进行对话。
注释:值known_scope将根据您可能的作用域中与标记匹配的作用域而有所不同。您的作用域列表将在其中一个endpoints.get_current_user() helper methods中循环,如果成功,则最终匹配的作用域将存储为os.getenv('OAUTH_LAST_SCOPE')。我强烈建议对known_scope使用此值。
更好的替代方案:
如前所述,App Engine用户ID根本不能从ID令牌中隐含出来(目前),但是可以使用Google Profile ID代替App Engine用户ID。(此ID通常被视为Google+ ID,尽管这在许多服务中是一致的)。
要确保此值与您的持有者或 ID令牌相关联,请确保还请求一个与userinfo API相关联的非userinfo.email作用域
https://www.googleapis.com/auth/plus.loginhttps://www.googleapis.com/auth/plus.mehttps://www.googleapis.com/auth/userinfo.emailhttps://www.googleapis.com/auth/userinfo.profile(此范围列表截至2013年5月20日撰写本文时的当前范围。)
与不记名令牌中的应用程序引擎用户ID类似,这个谷歌配置文件ID被endpoints.get_current_user(),丢弃,但它对这两种令牌都可用。
get_google_plus_user_id() method是appengine-picturesque-python示例的一部分,它修补了一个endpoints.get_current_user()帮助器方法以保留此数据,并允许您使用此值,而不必重复用于验证请求中的持有者或ID令牌的昂贵的网络调用。
发布于 2013-10-26 07:40:24
以防有人从1.8.6开始就在这里,并且仍然试图使用auth_util.py变通方法来返回谷歌配置文件id。endpoints.token_id现在有两种方法,具体取决于用户是否在开发服务器上。
当在谷歌的服务器上时,该流返回oauth_user,并且不会命中tokeninfo端点。因此,不会在auth_util.py中保存任何令牌信息。但是,在dev服务器上,它确实命中了tokeninfo端点,因此可以按预期工作。
对我来说,解决这个问题最简单的方法就是给endpoints.token_id._is_local_dev打补丁,并将其设置为总是正确的。
发布于 2014-04-13 23:48:14
我最近刚刚遇到了这种头痛。但是,经过一些测试后,似乎在本地开发服务器上使用1.9.2版本的user.user_id()会返回None。但是,如果您部署,它将返回一个值。但不确定这是应用程序引擎ID还是Google+ ID。不管怎样,要找出答案吗?
但是,考虑到文档,我假设它返回的ID用于持久记录是安全的。
https://stackoverflow.com/questions/16661109
复制相似问题