Решите, когда обновить токен OAUTH2 с помощью социальной аутентификации Python

Я считаю, что это в основном вопрос о лучших практик.

Я у поставщика OAuth2, маркеры доступа (до 10 часов) как маркеров обновления.

нашел здесь что довольно легко обновить маркер доступа, но я не могу понять, как решить, когда пришло время обновить.

легкий ответ, вероятно, "когда он больше не работает", что означает, когда я получаю HTTP 401 от бэкэнда. Проблема с это решение заключается в том, что оно не настолько эффективно, плюс я могу только предположить, что получил 401, потому что токен истек.

Я мое приложение django я нашел, что user social auth есть Extra data поле, содержащее что-то вроде этого:

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

но я не уверен, как использовать expires поле.

Итак, мой вопрос: Как узнать, истек ли токен доступа, и мне нужно его обновить?

редактировать: Я только что нашел этот комментарий это кажется актуальным, но я не могу понять, как подключить эту новую функцию в конвейере, чтобы работать во время обновления токена.

2 ответов


Я в конце концов понял это. Причина, по которой я изначально был смущен, заключалась в том, что на самом деле есть два случая:

  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

все еще в классе backend я добавил дополнительное поле для извлечения 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)

Я надеюсь, что это может быть полезным для других людей.


на 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 может истечь до выполнения дальнейшего кода.

более подробно дается в этом вопросе:как я могу обновить токен с помощью social-auth-app-django?