Решите, когда обновить токен 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?