简体   繁体   English

确定何时使用Python Social Auth刷新OAUTH2令牌

[英]Decide when to refresh OAUTH2 token with Python Social Auth

I believe this is mostly a question about best practices. 我认为这主要是关于最佳做法的问题。

I have an OAUTH2 provider that issues access tokens (valid for 10 hours) as long as refresh tokens. 我有一个OAUTH2提供程序,它会发出与刷新令牌一样长的访问令牌(有效期为10个小时)。

I found here that it is pretty easy to refresh the access token but I cannot understand how to decide when it is time to refresh. 我在这里发现刷新访问令牌非常容易,但是我不明白如何确定何时刷新。

The easy answer is probably "when it does not work any more", meaning when I get a HTTP 401 from the backend. 一个简单的答案可能是“何时不再起作用”,这意味着从后端获取HTTP 401时。 The problem with this solution is that it is not that efficient, plus I can only assume I got a 401 because the token has expired. 该解决方案的问题在于效率不高,而且我只能假定我已经获得了401,因为令牌已过期。

I my django app I found that the user social auth has an Extra data field containing something like this: 在我的django应用中,我发现user social authExtra data字段包含如下内容:

{ "scope": "read write", "expires": 36000, "refresh_token": "xxxxxxxxxxxxx", "access_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "token_type": "Bearer" }

but I am not sure how to use the expires field. 但我不确定如何使用expires字段。

So my question is: how do I know if an access token has expired and I need to refresh it? 所以我的问题是:我怎么知道访问令牌是否已过期并且需要刷新它?

EDIT : I just found this comment that seems relevant, but I cannot understand how to plug this new function in the pipeline in order to work during the token refresh. 编辑 :我刚刚发现此注释似乎相关,但我不明白如何在管道中插入此新功能以便在令牌刷新期间工作。

I eventually figured this out. 我最终想通了。 The reason I was initially confused was because there are actually two cases: 我最初感到困惑的原因是,实际上有两种情况:

  1. When the user comes from a login, so when basically the pipeline get executed. 当用户来自登录名时,基本上就可以执行管道。
  2. When the token is refreshed calling the user social auth method refresh_token 刷新令牌后,调用用户社交身份验证方法refresh_token

To solve the first case 解决第一种情况

I created a new function for the pipeline: 我为管道创建了一个新函数:

def set_last_update(details, *args, **kwargs):  # pylint: disable=unused-argument
    """
    Pipeline function to add extra information about when the social auth
    profile has been updated.
    Args:
        details (dict): dictionary of informations about the user
    Returns:
        dict: updated details dictionary
    """
    details['updated_at'] = datetime.utcnow().timestamp()
    return details

in the settings I added it in the pipeline right before the load_extra_data 在设置中,我在load_extra_data之前将其添加到管道中

SOCIAL_AUTH_PIPELINE = (
    'social.pipeline.social_auth.social_details',
    'social.pipeline.social_auth.social_uid',
    'social.pipeline.social_auth.auth_allowed',
    'social.pipeline.social_auth.social_user',
    'social.pipeline.user.get_username',
    'social.pipeline.user.create_user',
    'social.pipeline.social_auth.associate_user',
    # the following custom pipeline func goes before load_extra_data
    'backends.pipeline_api.set_last_update',
    'social.pipeline.social_auth.load_extra_data',
    'social.pipeline.user.user_details',
    'backends.pipeline_api.update_profile_from_edx',
    'backends.pipeline_api.update_from_linkedin',
)

and, still in the settings I added the new field in the extra data. 并且,仍然在设置中,我在额外数据中添加了新字段。

SOCIAL_AUTH_EDXORG_EXTRA_DATA = ['updated_at']

For the second case: 对于第二种情况:

I overwrote the refresh_token method of my backend to add the extra field. 我重写了后端的refresh_token方法以添加额外的字段。

def refresh_token(self, token, *args, **kwargs):
    """
    Overridden method to add extra info during refresh token.
    Args:
        token (str): valid refresh token
    Returns:
        dict of information about the user
    """
    response = super(EdxOrgOAuth2, self).refresh_token(token, *args, **kwargs)
    response['updated_at'] = datetime.utcnow().timestamp()
    return response

Still in the backend class, I added an extra field to extract the expires_in field coming from the server. 仍然在后端类中,我添加了一个额外的字段来提取来自服务器的expires_in字段。

EXTRA_DATA = [
    ('refresh_token', 'refresh_token', True),
    ('expires_in', 'expires_in'),
    ('token_type', 'token_type', True),
    ('scope', 'scope'),
]

At this point I have the timestamp when the access token has been created ( updated_at ) and the amount of seconds it will be valid ( expires_in ). 此时,我具有创建访问令牌的时间戳记( updated_at )和它将有效的秒数( expires_in )。

NOTE: the updated_at is an approximation, because it is created on the client and not on the provider server. 注意: updated_at是一个近似值,因为它是在客户端而不是在提供程序服务器上创建的。

Now the only thing missing is a function to check if it is time to refresh the access token. 现在唯一缺少的是用于检查是否该刷新访问令牌的功能。

def _send_refresh_request(user_social):
    """
    Private function that refresh an user access token
    """
    strategy = load_strategy()
    try:
        user_social.refresh_token(strategy)
    except HTTPError as exc:
        if exc.response.status_code in (400, 401,):
            raise InvalidCredentialStored(
                message='Received a {} status code from the OAUTH server'.format(
                    exc.response.status_code),
                http_status_code=exc.response.status_code
            )
        raise


def refresh_user_token(user_social):
    """
    Utility function to refresh the access token if is (almost) expired
    Args:
        user_social (UserSocialAuth): a user social auth instance
    """
    try:
        last_update = datetime.fromtimestamp(user_social.extra_data.get('updated_at'))
        expires_in = timedelta(seconds=user_social.extra_data.get('expires_in'))
    except TypeError:
        _send_refresh_request(user_social)
        return
    # small error margin of 5 minutes to be safe
    error_margin = timedelta(minutes=5)
    if datetime.utcnow() - last_update >= expires_in - error_margin:
        _send_refresh_request(user_social)

I hope this can be helpful for other people. 我希望这对其他人有帮助。

Currently, the extra_data field now has an auth_time . 当前, extra_data字段现在具有auth_time You can use this along with expires to determine the validity of the access_token as such: 您可以将其与expires一起使用,从而确定access_token的有效性:

if (social.extra_data['auth_time'] + social.extra_data['expires'] - 10) <= int(time.time()):
    from social_django.utils import load_strategy
    strategy = load_strategy()
    social.refresh_token(strategy)

The extra "10" seconds is in there to prevent a race condition where an access_token might expire before further code is executed. 多余的“ 10”秒用于防止出现争用情况,在这种情况下, access_token可能在执行进一步的代码之前到期。

More detail is given in this question: How can I refresh the token with social-auth-app-django? 这个问题给出了更多细节: 如何使用social-auth-app-django刷新令牌?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Python Social Auth、Google 和刷新令牌 - Python Social Auth, Google, and refresh token 在python中刷新Google云端硬盘(OAuth2)中的令牌 - Refresh token in Google Drive (OAuth2) in python 使用“ Python社交身份验证”更新后,无法刷新Google OAuth 2令牌 - Unable to refresh a Google OAuth 2 token after update using 'Python Social Auth' 找不到用于linkedin oauth2的python-social-auth后端 - python-social-auth backend not found for linkedin oauth2 django社交认证从Google oauth2获得错误的access_token - django social auth get wrong access_token from google oauth2 Xero:OAuth2:Python3:获取刷新令牌的示例代码 - Xero : OAuth2: Python3: Example Code to get the refresh token 在Google App Engine上使用python-social-auth进行的Google OAuth2身份验证失败 - Google OAuth2 authentication using python-social-auth on Google App Engine failed HTTPError 403(Forbidden),Django和python-social-auth通过OAuth2连接到Google - HTTPError 403 (Forbidden) with Django and python-social-auth connecting to Google with OAuth2 如何使用 python-social-auth 和 django-graphql-auth 返回刷新令牌? - How can I return the refresh token using python-social-auth and django-graphql-auth? 如何以编程方式刷新Google OAuth2令牌 - How to refresh Google OAuth2 token programatically
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM