无法授权使用服务帐户连接到自己的GAE端点API [英] Failing to authorize connection to own GAE endpoints API with service account
问题描述
我一直在想尽办法,试图成功授权我使用OAuth2和服务帐户通过python脚本运行的Google App Engine(GAE)项目上的API命中.
I've been beating my head against the wall trying to successfully authorize an API hit on the Google App Engine (GAE) project I'm running from a python script using OAuth2 and a service account.
我已经创建了服务帐户,将服务帐户ID添加到api文件中允许的客户端ID,将私钥从.p12转换为.pem,并授权了httplib2调用.我尝试使用.authorize()方法传递凭据,并通过将凭据加载为JSON并手动将access_token参数添加到标头中-{"Authorization": "Bearer " + token_string}.
I've created the service account, added the service account id to the allowed client ids in the api file, converted the private key from a .p12 to a .pem, and authorized the httplib2 call. I've tried passing the credentials using the .authorize() method and by loading the credentials as JSON and adding the access_token parameter to the headers manually -- {"Authorization": "Bearer " + token_string}.
每次调用时,API也会产生无效令牌".
Every call too the API yields "Invalid Token".
我注意到一个奇怪的事情,当我第一次调用SignedJwtAssertionCredentials时,凭据中没有访问令牌-"access_token"为无".但是,当我从存储中的.dat文件检索凭据时,访问令牌确实会显示.
One strange thing I have been noticed in that when I first call SignedJwtAssertionCredentials, there is no access token in the credentials -- "access_token" is None. However, the access token does show up when I retrieve the credentials from the .dat file in storage.
以下是GAE endpoints_api.py文件,python_test.py文件和401响应.
Following are the GAE endpoints_api.py file, the python_test.py file, and the 401 response.
任何想法都将不胜感激.
Any ideas would be hugely appreciated.
首先,应用引擎终结点服务器文件:
First the app engine endpoints server file:
# endpoints_api.py running on GAE
import endpoints
import time
from protorpc import messages
from protorpc import message_types
from protorpc import remote
SERVICE_ACCOUNT_ID = 'random_account_id_string.apps.googleusercontent.com'
WEB_CLIENT_ID = 'random_web_client_id_string.apps.googleusercontent.com'
ANDROID_AUDIENCE = WEB_CLIENT_ID
package = "MyPackage"
class Status(messages.Message):
message = messages.StringField(1)
when = messages.IntegerField(2)
class StatusCollection(messages.Message):
items = messages.MessageField(Status, 1, repeated=True)
STORED_STATUSES = StatusCollection(items=[
Status(message='Go.', when=int(time.time())),
Status(message='Balls.', when=int(time.time())),
Status(message='Deep!', when=int(time.time())),
])
@endpoints.api(name='myserver', version='v1')
class MyServerApi(remote.Service):
"""MyServer API v1."""
@endpoints.method(message_types.VoidMessage, StatusCollection,
allowed_client_ids=[SERVICE_ACCOUNT_ID,
endpoints.API_EXPLORER_CLIENT_ID],
audiences=[ANDROID_AUDIENCE],
scopes=[endpoints.EMAIL_SCOPE],
path='status', http_method='GET',
name='statuses.listStatus')
def statuses_list(self, unused_request):
current_user = endpoints.get_current_user()
if current_user is None:
raise endpoints.UnauthorizedException('Invalid token.')
else:
return current_user.email(), STORED_STATUSES
APPLICATION = endpoints.api_server([MyServerApi])
接下来是本地python脚本:
next the local python script:
# python_test.py file running from local server
from apiclient.discovery import build
from oauth2client.file import Storage
from oauth2client.client import SignedJwtAssertionCredentials
import httplib2
import os.path
import json
SERVICE_ACCOUNT_EMAIL = "service_account_string@developer.gserviceaccount.com"
ENDPOINT_URL = "http://my-project.appspot.com/_ah/api/myserver/v1/status"
SCOPE = 'https://www.googleapis.com/auth/userinfo.email'
SITE_ROOT = os.path.dirname(os.path.realpath(__file__))
f = file('%s/%s' % (SITE_ROOT, 'pk2.pem'), 'rb')
key = f.read()
f.close()
http = httplib2.Http()
storage = Storage('credentials.dat')
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = SignedJwtAssertionCredentials(
SERVICE_EMAIL, key, scope=SCOPE)
storage.put(credentials)
else:
credentials.refresh(http)
http = credentials.authorize(http)
headers = {'Content-Type': 'application/json'}
(resp, content) = http.request(ENDPOINT_URL,
"GET",
headers=headers)
print(resp)
print(content)
最后,控制台输出:
{'status': '401', 'alternate-protocol': '443:quic,p=0.002', 'content-length': '238', 'x- xss-protection': '1; mode=block', 'x-content-type-options': 'nosniff', 'transfer-encoding': 'chunked', 'expires': 'Sun, 14 Sep 2014 23:51:36 GMT', 'server': 'GSE', '-content-encoding': 'gzip', 'cache-control': 'private, max-age=0', 'date': 'Sun, 14 Sep 2014 23:51:36 GMT', 'x-frame-options': 'SAMEORIGIN', 'content-type': 'application/json; charset=UTF-8', 'www-authenticate': 'Bearer realm="https://accounts.google.com/AuthSubRequest"'}
{
"error": {
"errors": [
{
"domain": "global",
"reason": "required",
"message": "Invalid token.",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Invalid token."
}
}
推荐答案
感谢您的帮助.上面我做错了几件事.
Thank you for the help. There are a couple things I did wrong above.
首先,我需要在允许的ID列表中引用App Engine项目ID.
Firstly, I needed to reference the app engine project id in the allowed ids list.
API_ID = 'project-id-number.apps.googleusercontent.com'
@endpoints.method(message_types.VoidMessage, StatusCollection,
allowed_client_ids=[API_ID, SERVICE_ACCOUNT_ID,
endpoints.API_EXPLORER_CLIENT_ID],
audiences=[ANDROID_AUDIENCE],
scopes=[endpoints.EMAIL_SCOPE],
path='status', http_method='GET',
name='statuses.listStatus')
我错误地认为build()方法仅适用于官方google API,但事实证明我引用的项目不正确.有了项目ID后,我可以使用build()而不是在服务器端文件中写自己的httplib2调用(这不起作用).
I had incorrectly come to the assumption that the build() method would only work with official google APIS, but it turned out I was referencing my project incorrectly. With the project id in place, I could use build() instead of writing my own httplib2 call in the server side file (which didn't work).
删除"http =凭据.authorize(http)"下面的代码,我将其替换为以下内容:
Erasing the code beneath 'http = credentials.authorize(http)' I replaced it with the following:
myservice = build('my-service', 'v1', http=http, discoveryServiceUrl=discoveryServiceUrl)
data = myservice.my-path().endpoint-name()
results = data.execute()
这成功授权了我的帐户并呼叫了端点. !如果其他人对此有疑问,或者我的解决方案不清楚,请随时发表评论.我不希望这是一个痛苦的过程.
This successfully authorizes my account and calls the endpoint. Woot! If anyone else has questions about this, or if my solution isn't clear, please feel free to comment. This was a painful process I don't wish upon anyone else.
这篇关于无法授权使用服务帐户连接到自己的GAE端点API的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!