Решите, когда обновить токен 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 ответов
Я в конце концов понял это. Причина, по которой я изначально был смущен, заключалась в том, что на самом деле есть два случая:
- когда пользователь приходит из логина, поэтому, когда в основном выполняется конвейер.
 - при обновлении токена вызывается метод социальной аутентификации пользователя 
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?