创建会话时如何检测过期的访问令牌?

Posted

技术标签:

【中文标题】创建会话时如何检测过期的访问令牌?【英文标题】:How to detect expired access tokens when creating the session? 【发布时间】:2013-04-13 17:04:55 【问题描述】:

我有以下测试程序:

from   rauth.service import OAuth1Service, OAuth2Service

SUPPORTED_SERVICES = 
    'twitter'  : ( 'OAuth1', 'twitter',  'https://api.twitter.com/oauth',        'request_token', 'access_token', 'authorize', 'https://api.twitter.com/1/',  None),
    'facebook' : ( 'OAuth2', 'facebook', 'https://graph.facebook.com/oauth',     None,            'access_token', 'authorize', 'https://graph.facebook.com/', 'https://www.facebook.com/connect/login_success.html'),
    'google'   : ( 'OAuth2', 'google',   'https://accounts.google.com/o/oauth2', None,            'token',        'auth',      None,                          'http://localhost'),


CLIENT_DATA = 
    'twitter'  : ('dummy_client_id', 'dummy_client_secret'),
    'facebook' : ('dummy_client_id', 'dummy_client_secret'),
    'google'   : ('dummy_client_id', 'dummy_client_secret'),


USER_TOKENS = 
    'user1' : 
        'twitter'  : ('dummy_access_token', 'dummy_access_token_secret'),
        'facebook' : ('dummy_access_token', None),
        'google'   : ('dummy_access_token', None),
    


def test_google(user_id):
    service_id = 'google'
    oauthver, name, oauth_base_url, request_token_url, access_token_url, authorize_url, base_url, redirect_uri = SUPPORTED_SERVICES[service_id]
    request_token_url = oauth_base_url + '/' + (request_token_url or '')
    access_token_url  = oauth_base_url + '/' + access_token_url
    authorize_url     = oauth_base_url + '/' + authorize_url
    client_id, client_secret = CLIENT_DATA[service_id]
    google = OAuth2Service(
        client_id=client_id,
        client_secret=client_secret,
        name=name,
        authorize_url=authorize_url,
        access_token_url=access_token_url,
        base_url=base_url)
    access_token, access_token_secret = USER_TOKENS[user_id][service_id] # access_token_secret only needed for twitter (OAuth1)
    session = google.get_session(access_token)
    user = session.get('https://www.googleapis.com/oauth2/v1/userinfo').json()
    print user

test_google('user1')

我已授权我的应用程序访问user1 的google 帐户,并获得了一个access_token。该访问令牌已经过期,我的程序的输出是:

u'error': u'code': 401, u'message': u'Invalid Credentials', u'errors': [u'locationType': u'header', u'domain': u'global', u'message': u'Invalid Credentials', u'reason': u'authError', u'location': u'Authorization']

我想在创建会话时检查访问令牌是否已过期,不是在请求数据时。这可能吗? 如何验证会话对象是否真的被授权?

为了澄清,我想做的是以下几点:

    首先,让用户授权我的应用程序 保存访问令牌以备将来使用(在数据库中,但在测试代码中,这是在脚本中硬编码的) 当以后的访问检测到令牌已过期时,返回步骤 1

我目前在执行第 3 步时遇到问题。我当然可以检测到对我的 GET 的 json 回复中的 401,但是强制验证所有 GET 访问看起来相当麻烦。我想要做的是在创建会话时验证会话是否真正处于活动状态,然后假设在会话对象的整个持续时间内它将保持活动状态。通常这只是几毫秒,而我的 web 应用正在处理请求并使用 OAuth 会话对象访问 google API。

【问题讨论】:

"我想在创建会话时检查访问令牌是否已过期,而不是在请求数据时。"这不可能。您应该使用授权用户时返回的到期时间。这意味着使用 get_raw_access_token 并检查完整的响应。 这很奇怪(我相信你!)。但是我在创建会话时发送access_token。我假设图书馆在内部对服务提供商(在这种情况下是谷歌)执行一些请求。并且服务提供者知道令牌已经过期。为什么在回复会话创建请求时不这么说?这是服务提供商问题还是图书馆问题?可能我误解了创建会话是什么意思,现阶段与服务提供者之间并没有真正的沟通。 当您调用get_session 并提供访问令牌时,没有发生网络通信;这只是使用该访问令牌创建一个新的会话对象。如果您使用get_auth_session,则会进行网络调用,但没有针对过期错误的标准格式,因此库无法自动处理。 【参考方案1】:

您必须先调用get_authorization_url,该用户必须打开并授予您访问其帐户的权限,作为回报,您将从redirect_uri回调的查询参数中获得一个代码,您可以将其换成access_token

params = 
    'scope': 'email',
    'response_type': 'code',
    'redirect_uri': redirect_uri,
    'access_type': 'offline', # to get refresh_token


print google.get_authorize_url(**params)

根据documentation,这段代码应该可以工作:

data = 
    'code': 'code you got from callback',
    'grant_type': 'authorization_code',
    'redirect_uri': 'http://localhost/oauth2',


response = google.get_raw_access_token(data=data)

作为响应,您将获得如下 JSON 数据:


  "access_token" : "ya29.AHE<....>n3w",
  "token_type" : "Bearer",
  "expires_in" : 3600,
  "id_token" : "eyJh<...>QwNRzc",
  "refresh_token" : "1/X86S<...>Vg4"

如您所见,有expires_in(秒),您必须存储获得令牌的时间并稍后与当前时间+expires_in进行比较。

如果令牌过期,您可以稍后使用refresh_token 刷新它,而无需再次要求用户确认:

response = google.get_raw_access_token(data=
    'refresh_token': refresh_token,
    'grant_type': 'refresh_token',
)
print response.content

请注意,refresh_token 只会在用户首次授权应用时返回。 详情请见this question。

唉,您似乎不能使用get_auth_session,因为在内部它只提取access_token,而其他所有内容都被丢弃了。

如果您立即获得access_token 而没有首先获得身份验证code,您仍然会在回调中获得expires_in。来自docs:

https://oauth2-login-demo.appspot.com/oauthcallback#access_token=1/fFBGRNJru1FQd44AzqT3Zg&token_type=Bearer&expires_in=3600

【讨论】:

你说得对,get_auth_session 不能用于此(顺便说一句,很好的解释);我考虑过稍微改变这种机制,以便将响应中的数据保留在会话中,以便允许这样做,但我认为它需要更多的思考。 在 kwargs 中检查回调怎么样?如果存在,请使用数据调用它。一方面它根本不会弄乱内部结构,另一方面它对用户来说不是很方便。我想,我会按照你的方式做,只需将原始回复放入会话中即可。 我认为问题在于库假设您正在使用某种服务器,这不一定是 rauth 对其用户施加的要求:您可以在 Web 上下文之外使用 rauth应用程序,例如。 回调我的意思是一个python函数,它接收解析的响应作为参数,不需要服务器或任何东西。无论如何,回调不是一个好主意。 Google 的 oauth2 库在其会话对象中存储 refresh_token 和 expire_in 时间,并在需要和可能的情况下自动刷新。 在某处有refresh_token 的例子吗?我在 API (rauth.readthedocs.org/en/latest/api) 中找不到它,我在代码中找不到它...

以上是关于创建会话时如何检测过期的访问令牌?的主要内容,如果未能解决你的问题,请参考以下文章

Webapi 2.0如何在访问令牌过期时实现刷新JWT令牌

如何在 React 上检测 jwt 令牌过期

Spring Boot:过期后如何生成新的访问令牌?

如何使用spring检测tomcat会话过期

如何测试 Azure AD 访问令牌过期

如何从刷新令牌中获取访问令牌?刷新令牌会过期吗?