google auth app python django oauth-2.0 python-social-auth

python - app - social auth django google



Decida cuándo actualizar el token OAUTH2 con Python Social Auth (2)

Creo que esto es principalmente una pregunta sobre las mejores prácticas.

Tengo un proveedor OAUTH2 que emite tokens de acceso (válido durante 10 horas) siempre que actualice los tokens.

Encontré aquí que es bastante fácil actualizar el token de acceso, pero no puedo entender cómo decidir cuándo es el momento de actualizar.

La respuesta fácil es probablemente "cuando ya no funciona", es decir, cuando obtengo un HTTP 401 desde el servidor. El problema con esta solución es que no es tan eficiente, y solo puedo suponer que obtuve un 401 porque el token ha expirado.

Mi aplicación django descubrí que la user social auth del user social auth tiene un campo de Extra data contiene algo como esto:

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

pero no estoy seguro de cómo usar el campo expires .

Entonces mi pregunta es: ¿cómo sé si un token de acceso ha expirado y necesito actualizarlo?

EDITAR : Acabo de encontrar este comentario que parece relevante, pero no puedo entender cómo conectar esta nueva función en la tubería para trabajar durante la actualización del token.


Finalmente me di cuenta de esto. La razón por la que inicialmente estaba confundido fue porque en realidad hay dos casos:

  1. Cuando el usuario proviene de un inicio de sesión, cuando básicamente se ejecuta la canalización.
  2. Cuando el token se actualiza llamando al método social auth user refresh_token

Para resolver el primer caso

Creé una nueva función para la tubería:

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

en la configuración lo agregué en la tubería justo antes de 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'', )

y, aún en la configuración, agregué el nuevo campo en los datos adicionales.

SOCIAL_AUTH_EDXORG_EXTRA_DATA = [''updated_at'']

Para el segundo caso:

refresh_token método refresh_token de mi backend para agregar el campo extra.

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

Todavía en la clase backend, agregué un campo extra para extraer el campo expires_in viene del servidor.

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

En este punto, tengo la marca de tiempo cuando se ha creado el token de acceso ( updated_at ) y la cantidad de segundos será válido ( expires_in ).

NOTA: el updated_at es una aproximación, porque se crea en el cliente y no en el servidor del proveedor.

Ahora, lo único que falta es una función para verificar si es el momento de actualizar el token de acceso.

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)

Espero que esto pueda ser útil para otras personas.


Actualmente, el campo extra_data ahora tiene auth_time . Puede usar esto junto con expires para determinar la validez de access_token como tal:

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)

Los "10" segundos adicionales están ahí para evitar una condición de carrera donde un access_token podría caducar antes de que se ejecute más código.

Se brindan más detalles en esta pregunta: ¿cómo puedo actualizar el token con social-auth-app-django?