如何使用 social-auth-app-django 刷新令牌?

How can I refresh the token with social-auth-app-django?

我使用 Python Social Auth - Django 登录我的用户。

我的后端是 Microsoft,所以我可以使用 Microsoft Graph,但我认为它不相关。

Python Social Auth 处理身份验证,但现在我想调用 API,为此,我需要一个有效的访问令牌。 按照 use cases 我可以得到这个:

social = request.user.social_auth.get(provider='azuread-oauth2')
response = self.get_json('https://graph.microsoft.com/v1.0/me',
                         headers={'Authorization': social.extra_data['token_type'] + ' ' 
                                                   + social.extra_data['access_token']})

但是访问令牌的有效期只有3600秒,所以我需要刷新,我想我可以手动完成,但必须有更好的解决方案。 如何让 access_token 刷新?

使用 load_strategy()social.apps.django_app.utils:

social = request.user.social_auth.get(provider='azuread-oauth2')
strategy = load_strategy()
social.refresh_token(strategy)

现在可以从 social.extra_data['access_token'] 中检索更新的 access_token

最好的方法可能是检查它是否需要更新(为 AzureAD Oauth2 定制):

def get_azuread_oauth2_token(user):
    social = user.social_auth.get(provider='azuread-oauth2')
    if social.extra_data['expires_on'] <= int(time.time()):
        strategy = load_strategy()
        social.refresh_token(strategy)
    return social.extra_data['access_token']

这是基于 AzureADOAuth2 中的 get_auth_token 方法。我认为这种方法在管道外是无法访问的,如果有任何方法,请回答这个问题。

更新

更新 1 - 2017 年 1 月 20 日

Issue 之后请求一个带有访问令牌刷新时间的额外数据参数,现在可以检查 access_token 是否需要在每个后端更新。

在未来的版本中(>0.2.1 for the social-auth-core)将在额外数据中添加一个新字段:

'auth_time': int(time.time())

这样就可以了:

def get_token(user, provider):
    social = user.social_auth.get(provider=provider)
    if (social.extra_data['auth_time'] + social.extra_data['expires']) <= int(time.time()):
        strategy = load_strategy()
        social.refresh_token(strategy)
    return social.extra_data['access_token']

注意:根据OAuth 2 RFC,所有响应(这是推荐参数)都应提供expires_in,但对于大多数后端(包括azuread-oauth2) 这个值被保存为 expires。请仔细了解您的后端行为! 关于此的 Issue 存在,我会在它存在时用相关信息更新答案。

更新 2 - 2017 年 2 月 17 日

此外,UserMixin 中有一个名为 access_token_expired 的方法(code) that can be used to assert if the token is valid or not (note: this method doesn't work for race conditions, as pointed out in by @SCasey)。

更新 3 - 2017 年 5 月 31 日

Python Social Auth - Core v1.3.0get_access_token(self, strategy) 是在 storage.py 中引入的。

所以现在:

from social_django.utils import load_strategy

social = request.user.social_auth.get(provider='azuread-oauth2')
response = self.get_json('https://graph.microsoft.com/v1.0/me',
                     headers={'Authorization': '%s %s' % (social.extra_data['token_type'], 
                                                          social.get_access_token(load_strategy())}

感谢 @damio 指出。

@NBajanca 的更新对于版本 1.0.1 几乎是正确的。

extra_data['expires_in']

现在是

extra_data['expires']

所以代码是:

def get_token(user, provider):
    social = user.social_auth.get(provider=provider)
    if (social.extra_data['auth_time'] + social.extra_data['expires']) <= int(time.time()):
        strategy = load_strategy()
        social.refresh_token(strategy)
    return social.extra_data['access_token']

我还建议从该计算中减去任意数量的时间,这样我们就不会 运行 进入我们在到期前 0.01 秒检查令牌然后得到错误,因为我们在过期后发送了请求。为了安全起见,我喜欢加 10 秒,但这可能有点过头了:

def get_token(user, provider):
    social = user.social_auth.get(provider=provider)
    if (social.extra_data['auth_time'] + social.extra_data['expires'] - 10) <= int(time.time()):
        strategy = load_strategy()
        social.refresh_token(strategy)
    return social.extra_data['access_token']

编辑 @NBajanca 指出 expires_in 根据 Oauth2 文档在技术上是正确的。似乎对于某些后端,这可能有效。从 v1.0.1

开始,上面使用 expires 的代码适用于 provider="google-oauth2"

.get_access_token(strategy) 令牌过期后自动刷新。你可以这样使用它:

from social_django.utils import load_strategy
#...
social = request.user.social_auth.get(provider='google-oauth2')
access_token = social.get_access_token(load_strategy())