【问题标题】:Decide when to refresh OAUTH2 token with Python Social Auth决定何时使用 Python Social Auth 刷新 OAUTH2 令牌
【发布时间】:2016-09-04 11:29:57
【问题描述】:

我认为这主要是关于最佳实践的问题。

我有一个 OAUTH2 提供程序,只要刷新令牌,它就会颁发访问令牌(有效期 10 小时)。

我发现here 刷新访问令牌非常容易,但我不明白如何决定何时刷新。

简单的答案可能是“当它不再工作时”,这意味着当我从后端获得 HTTP 401 时。 这个解决方案的问题是效率不高,而且我只能假设我得到了 401,因为令牌已过期。

我在我的 django 应用程序中发现 user social auth 有一个 Extra data 字段,其中包含以下内容:

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

但我不确定如何使用 expires 字段。

所以我的问题是:我如何知道访问令牌是否已过期并且我需要刷新它?

编辑: 我刚刚发现似乎相关的this comment,但我不明白如何在管道中插入这个新功能以便在令牌刷新期间工作。

【问题讨论】:

    标签: python django oauth-2.0 python-social-auth


    【解决方案1】:

    我最终想通了。 我最初感到困惑的原因是因为实际上有两种情况:

    1. 当用户来自登录时,基本上管道得到执行时。
    2. 刷新令牌时调用用户社交身份验证方法refresh_token

    解决第一种情况

    我为管道创建了一个新函数:

    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
    

    在设置中,我在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',
    )
    

    而且,我仍然在设置中的额外数据中添加了新字段。

    SOCIAL_AUTH_EDXORG_EXTRA_DATA = ['updated_at']
    

    对于第二种情况:

    我重写了后端的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
    

    仍然在后端类中,我添加了一个额外的字段来提取来自服务器的expires_in 字段。

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

    此时我有创建访问令牌时的时间戳 (updated_at) 以及它将有效的秒数 (expires_in)。

    注意:updated_at 是一个近似值,因为它是在客户端而不是提供者服务器上创建的。

    现在唯一缺少的是一个检查是否该刷新访问令牌的函数。

    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)
    

    我希望这对其他人有帮助。

    【讨论】:

    • 我应该在哪里创建函数??
    • @mohreza 哪一个?
    【解决方案2】:

    目前,extra_data 字段现在有一个 auth_time。您可以将其与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)
    

    额外的“10”秒是为了防止出现access_token 可能在执行进一步代码之前到期的竞争条件。

    更多细节在这个问题中给出:How can I refresh the token with social-auth-app-django?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-18
      • 1970-01-01
      • 2017-05-18
      • 2015-01-03
      • 1970-01-01
      • 2021-08-24
      • 2012-10-06
      • 1970-01-01
      相关资源
      最近更新 更多