gdata OAuth2如何使用多个用户的令牌
Posted
技术标签:
【中文标题】gdata OAuth2如何使用多个用户的令牌【英文标题】:gdata OAuth2 how to work with multiple user's tokens 【发布时间】:2013-01-26 15:40:39 【问题描述】:场景:
我在授予访问权限时将令牌保存在每个应用用户的数据存储区中。
我需要创建多个 GData 联系人授权客户,以便 访问不同用户的联系人,我得到所需用户的令牌 从数据存储中创建授权联系人客户端,例如复制一个用户的 联系人到其他用户的联系人。
问题:
授权客户端按预期工作并获取 使用其令牌的用户,但使用令牌的令牌到期日期也是 已保存,它会在一小时或 45 分钟后过期,然后它就不再起作用了, 我读到如果令牌对象中有刷新令牌,那么令牌 如果短期访问令牌是,将自动获取新的访问令牌 已过期,但没有发生这种情况。
问题:
如果从数据存储中获取令牌,我如何刷新令牌 已过期,这样我就可以访问用户的联系人,只要用户不 撤销访问权限?
代码:
SCOPE = 'https://www.google.com/m8/feeds' REDIRECT_URI = 'http://localhost:8888/mainPage' USER_AGENT = 'shared-contacts' token = gdata.gauth.OAuth2Token(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, scope=SCOPE, user_agent=USER_AGENT) class MyEntity(db.Model): userEmail = db.StringProperty() access_token = ObjectProperty() class ObjectProperty(db.BlobProperty): def validate(self, value): try: result = pickle.dumps(value) return value except pickle.PicklingError, e: return super(ObjectProperty, self).validate(value) def get_value_for_datastore(self, model_instance): result = super(ObjectProperty, self).get_value_for_datastore(model_instance) result = pickle.dumps(result) return db.Blob(result) def make_value_from_datastore(self, value): try: value = pickle.loads(str(value)) except: pass return super(ObjectProperty, self).make_value_from_datastore(value) class MainHandler(webapp2.RequestHandler): def get(self): user = users.get_current_user() if user: self.redirect(token.generate_authorize_url( redirect_uri=REDIRECT_URI, access_type='offline') ) else: self.redirect(users.create_login_url("/")) class MainPage(webapp2.RequestHandler): def get(self): # Getting the token from datastore, if token exists for this user then # proceed otherwise get the access token and save in datastore. accessToken = Fetch_Access_Token() if accessToken: pass else: url = atom.http_core.Uri.parse_uri(self.request.uri) code = url.query code = code['code'] if 'error' in code: # User Did Not Grant The Access pass else: token_user_email = users.get_current_user().email() token.GetAccessToken(code) entity = MyEntity(userEmail=token_user_email, access_token=token) entity.put() self.response.out.write(template.render('index.html',)) class RPCMethods(): def MoveContacts(self, *args): # All tokens except current user shared_group_users = shared_users(skip_current_user) contact_client = gdata.contacts.client.ContactsClient(source=USER_AGENT) for sg_user in shared_group_users.itervalues(): shared_user_token = sg_user[1] # PROBLEM OCCURS HERE, IT WORKS FOR ONLY 1 HOUR AND HERE I NEED TO REFRESH THIS TOKEN. # For almost an hour i get other user's Data, but after that i get current user's Data. shared_user_authorized_client = shared_user_token.authorize(contact_client) shared_group_contact_feed = shared_user_authorized_client.GetContacts() application = webapp2.WSGIApplication([ ('/', MainHandler), ('/mainPage', MainPage), ('/rpc', RPCHandler), ], debug=True)
更新:
工作代码:
shared_user_credentials = StorageByKeyName(CredentialsModel, shared_user_credentials_key, 'credentials').get()
if shared_user_credentials.access_token_expired == True:
http = httplib2.Http()
http = shared_user_credentials.authorize(http)
shared_user_credentials.refresh(http)
【问题讨论】:
您如何将其存储在数据存储中?你在执行什么来到达atom.http_core.Uri.parse_uri(self.request.uri)
?
@bossylobster 我使用 gdata.gauth.OAuth2Token 获得的令牌对象,我无法使用应用引擎中提供的方法将其保存在数据存储中,例如ae_save 或数据存储 CredentialsProperty(),所以我搜索 n 找到了一个解决方案。对您的问题的回答对于评论来说太长了,所以我已经编辑了问题并添加了所有代码,请看一下,谢谢
【参考方案1】:
所以 OAuth 2.0 代码“完全没问题”,但是您在后续需要它时获取令牌的方式是问题。
在另一个post 中,我提到了OAuth2.0 文档的offline access 部分状态:
当您的应用程序收到刷新令牌时,重要的是 存储该刷新令牌以供将来使用。如果您的申请丢失 刷新令牌,它必须重新提示用户同意 获取另一个刷新令牌。如果您需要重新提示用户 同意,在授权中包含
approval_prompt
参数 代码请求,并将值设置为force
。
简单修复:
所以当你打电话给generate_authorize_url
时,发送approval_prompt='force'
:
self.redirect(token.generate_authorize_url(redirect_uri=REDIRECT_URI,
access_type='offline',
approval_prompt='force')
您会注意到approval_prompt
已经在generate_authorize_url
方法的signature 中。
实际修复:
当您存储令牌时:
token_user_email = users.get_current_user().email()
token.GetAccessToken(code)
entity = MyEntity(userEmail=token_user_email, access_token=token)
您知道您将为当前用户设置一个实体。我建议将其存储为
entity = MyEntity(id=token_user_email, access_token=token).
当您让您的用户完成 OAuth 2.0 流程时,
user = users.get_current_user()
if user:
self.redirect(token.generate_authorize_url(
...
您应该首先检查给定用户是否已有令牌:
token = MyEntity.get_by_id(user.email())
if token is not None:
# do something
你真的应该,真的,REALLY,不要使用token
作为一个全局对象!
老实说,有很多方法可以解决这个问题,您最好使用来自google-api-python-client
的OAuth2Decorator
。
有关如何将此装饰器与gdata-python-client
结合使用的一些参考,请查看我在另一个问题上发布的answer。
离别说明:
您应该使用ndb
datastore library。不必编写那个可怕的ObjectProperty
,您可以简单地使用ndb.PickleProperty
,而无需您付出任何努力。
【讨论】:
非常感谢,点击“有用的答案”,但没有足够的声誉,我说:S 我现在正在使用您的帖子“在 GData 和 Discovery 之间桥接 OAuth 2.0 对象”尝试使用装饰器的 oauth 流程。从 bd 切换到 ndb 现在我什至不需要保存令牌,因为“OAuth2TokenFromCredentials”将拥有一个。希望这次令牌会被刷新,因为我无法提示用户使用“approval_prompt”在每次刷新时授予访问权限,因为我需要创建许多联系人客户端来处理他们的联系人 现在无法使用 storage = StorageByKeyName(CredentialsModel, user.user_id(), 'credentials'), storage.put(decorator.credentials) 来保存凭证对象 从数据存储中获取对象“凭据”后使用装饰器,现在如果访问令牌过期,我可以刷新它。下一个问题是刷新令牌何时到期,我已阅读其有效期为 14 天。更新问题 n 包括新代码,非常感谢您的帮助以及最好和最有用的文章之一,桥接发现 n gdata。当我再次遇到麻烦时会再次打扰你:)以上是关于gdata OAuth2如何使用多个用户的令牌的主要内容,如果未能解决你的问题,请参考以下文章
如何以管理员用户身份撤销用户的访问令牌和刷新令牌?在 Oauth2 中使用 JWT
如何安全地存储 Discord(OAuth2) 用户的访问令牌?
如何基于使用 Oauth2 协议的身份验证改进 JWT 访问令牌和刷新令牌?