google - ¿Cómo puedo usar Django OAuth Toolkit con Python Social Auth?
social-auth-app-django google (3)
Estoy construyendo una API usando Django Rest Framework. Más tarde, esta API se supone que será consumida por dispositivos iOS y Android. Quiero permitir que mis usuarios se registren con proveedores de oauth2 como Facebook y Google. En este caso, no deberían tener que crear una cuenta con mi plataforma. Pero los usuarios también deberían poder registrarse cuando no tengan una cuenta de Facebook / Google, para lo cual estoy usando django-oauth-toolkit, entonces tengo mi propio proveedor de oauth2.
Para proveedores externos estoy usando python-social-auth, que funciona bien y crea automáticamente los objetos del usuario.
Quiero que los clientes se autentiquen mediante el uso de tokens de portador, lo que funciona bien para los usuarios que se registraron con mi proveedor (django-oauth-toolkit proporciona el esquema de autenticación y las clases de permisos para Django REST Framework).
Sin embargo, python-social-auth solo implementa la autenticación basada en sesiones, por lo que no existe una forma directa de realizar solicitudes API autenticadas en nombre de usuarios que hayan sido registradas por un proveedor externo de oauth2.
Si uso un access_token que ha sido generado por django-oauth-toolkit, hacer una solicitud como esta funciona:
curl -v -H "Authorization: Bearer <token_generated_by_django-oauth-toolkit>" http://localhost:8000/api/
Sin embargo, lo siguiente no funciona, ya que no existe un esquema de autenticación correspondiente para Django REST Framework y AUTHENTICATION_BACKENDS proporcionados por python-social-auth, solo funcionan para la autenticación basada en sesión:
curl -v -H "Authorization: Bearer <token_stored_by_python-social-auth>" http://localhost:8000/api/
El uso de la API navegable proporcionada por Django REST Framework después de la autenticación con python-social-auth funciona bien, solo las llamadas API sin una cookie de sesión no funcionan.
Me pregunto cuál es el mejor enfoque para este problema. Como yo lo veo, básicamente tengo dos opciones:
R: Cuando un usuario se registra con un proveedor externo de oauth2 (manejado por python-social-auth), enganche en el proceso para crear un oauth2_provider.models.AccessToken y continúe usando ''oauth2_provider.ext.rest_framework.OAuth2Authentication''
, ahora autenticando también usuarios que se registraron con un proveedor externo. Este enfoque se sugiere aquí: https://groups.google.com/d/msg/django-rest-framework/ACKx1kY7kZM/YPWFA2DP9LwJ
B: utilice python-social-auth para la autenticación de solicitud API. Pude conseguir mis propios usuarios en python-social-auth escribiendo un backend personalizado y usando register_by_access_token. Sin embargo, dado que las llamadas API no pueden utilizar las sesiones de Django, esto significaría que tendría que escribir un esquema de autenticación para Django Rest Framework que utiliza los datos almacenados por python-social-auth. Algunos consejos sobre cómo hacer esto se pueden encontrar aquí:
http://psa.matiasaguirre.net/docs/use_cases.html#signup-by-oauth-access-token
http://blog.wizer.fr/2013/11/angularjs-facebook-with-a-django-rest-api/
http://cbdev.blogspot.it/2014/02/facebook-login-with-angularjs-django.html
Sin embargo, según entiendo, python-social-auth solo verifica el token cuando inicia sesión y luego se basa en la sesión de Django. Esto significaría que tendría que encontrar una manera de evitar que python-social-auth realice todo el flujo de oauth2 para cada solicitud de API sin estado y en su lugar comparar con los datos almacenados en el DB, que en realidad no está optimizado para consultas ya que es almacenado como JSON (podría usar UserSocialAuth.objects.get (extra_data__contains =)).
También tendría que encargarme de verificar los alcances de un token de acceso y usarlos para verificar los permisos, algo que django-oauth-toolkit ya hace ( TokenHasScope
, required_scopes
, etc.).
Por el momento, me inclino por utilizar la opción A, ya que django-oauth-toolkit ofrece una buena integración con Django Rest Framework y obtengo todo lo que necesito de manera inmediata. El único inconveniente es que tengo que "inyectar" las access_tokens recuperadas por python-social-auth en el modelo AccessToken de django-oauth-toolkit, lo que de alguna manera parece estar mal, pero probablemente sería el enfoque más fácil.
¿Alguien tiene alguna objeción al hacerlo o quizás ha abordado el mismo problema de una manera diferente? ¿Me estoy perdiendo algo obvio y estoy haciendo mi vida más difícil de lo necesario? Si alguien ya ha integrado django-oauth-toolkit con python-social-auth y proveedores externos de oauth2 estaría muy agradecido por algunos consejos u opiniones.
Gran parte de la dificultad en la implementación de OAuth se reduce a la comprensión de cómo se supone que funciona el flujo de autorización. Esto se debe principalmente a que este es el "punto de partida" para iniciar sesión, y cuando se trabaja con un backend de terceros (usando algo como Python Social Auth) lo haces dos veces : una para tu API y otra para el tercero. API.
Autorizando solicitudes usando su API y un back-end de terceros
El proceso de autenticación que necesita es:
Mobile App -> Your API : Authorization redirect
Your API -> Django Login : Displays login page
Django Login -> Facebook : User signs in
Facebook -> Django Login : User authorizes your API
Django Login -> Your API : User signs in
Your API -> Mobile App : User authorizes mobile app
Estoy usando "Facebook" como back-end de terceros aquí, pero el proceso es el mismo para cualquier back-end.
Desde la perspectiva de su aplicación móvil, solo está redirigiendo a la url /authorize
proporcionada por Django OAuth Toolkit . Desde allí, la aplicación móvil espera hasta que se llegue a la url de devolución de llamada, al igual que en el flujo de autorización estándar de OAuth. Casi todo lo demás (inicio de sesión de Django, inicio de sesión social, etc.) es manejado por Django OAuth Toolkit o Python Social Auth en segundo plano.
Esto también será compatible con casi todas las librerías OAuth que use, y el flujo de autorización funcionará de la misma forma sin importar qué backend de terceros se use. Incluso manejará el caso (común) en el que necesite ser capaz de soportar el back-end de autenticación de Django (correo electrónico / nombre de usuario y contraseña), así como un inicio de sesión de un tercero.
Mobile App -> Your API : Authorization redirect
Your API -> Django Login : Displays login page
Django Login -> Your API : User signs in
Your API -> Mobile App : User authorizes mobile app
Lo que también es importante tener en cuenta aquí es que la aplicación móvil (que podría ser cualquier cliente de OAuth) nunca recibe los tokens de OAuth de Facebook / terceros . Esto es increíblemente importante, ya que asegura que su API actúe como intermediario entre el cliente de OAuth y las cuentas sociales de los usuarios.
Mobile App -> Your API : Authorization redirect
Your API -> Mobile App : Receives OAuth token
Mobile App -> Your API : Requests the display name
Your API -> Facebook : Requests the full name
Facebook -> Your API : Sends back the full name
Your API -> Mobile App : Send back a display name
De lo contrario, el cliente de OAuth podría omitir su API y realizar solicitudes en su nombre a las API de terceros.
Mobile App -> Your API : Authorization redirect
Your API -> Mobile App : Receives Facebook token
Mobile App -> Facebook : Requests all of the followers
Facebook -> Mobile App : Sends any requested data
Notarás que en este momento habrás perdido todo el control sobre los tokens de terceros . Esto es especialmente peligroso porque la mayoría de los tokens pueden acceder a una amplia gama de datos, lo que abre la puerta al abuso y, finalmente, pasa a su nombre . Lo más probable es que aquellos que inicien sesión en su API / sitio web no tengan la intención de compartir su información social con el cliente de OAuth, y en su lugar esperan que mantenga esa información privada (tanto como sea posible), sino que la expone a todos .
Autenticando solicitudes a su API
Cuando la aplicación móvil utiliza su token OAuth para realizar solicitudes a su API , toda la autenticación se realiza a través de Django OAuth Toolkit (o su proveedor OAuth) en segundo plano. Todo lo que ves es que hay un User
asociado con tu solicitud.
Mobile App -> Your API : Sends request with OAuth token
Your API -> Django OAuth Toolkit : Verifies the token
Django OAuth Toolkit -> Your API : Returns the user who is authenticated
Your API -> Mobile App : Sends requested data back
Esto es importante, porque después de la etapa de autorización no debería marcar la diferencia si el usuario proviene de Facebook o del sistema de autenticación de Django . Su API solo necesita un User
para trabajar, y su proveedor OAuth debería poder manejar la autenticación y verificación del token.
Esto no es muy diferente de cómo Django REST framework autentica al usuario cuando usa autenticación respaldada por sesión.
Web Browser -> Your API : Sends session cookie
Your API -> Django : Verifies session token
Django -> Your API : Returns session data
Your API -> Django : Verifies the user session
Django -> Your API : Returns the logged in user
Your API -> Web Browser : Returns the requested data
Nuevamente, todo esto se maneja con Django OAuth Toolkit y no requiere trabajo adicional para implementar.
Trabajando con un SDK nativo
En la mayoría de los casos, va a autenticar al usuario a través de su propio sitio web y usar Python Social Auth para manejar todo. Pero la única excepción notable es cuando se utiliza un SDK nativo, ya que la autenticación y la autorización se manejan a través del sistema nativo , lo que significa que está pasando por alto su API por completo . Esto es ideal para aplicaciones que necesitan iniciar sesión con un tercero, o aplicaciones que no usan su API en absoluto, pero es una pesadilla cuando ambas se unen .
Esto se debe a que su servidor no puede validar el inicio de sesión y se ve forzado a suponer que el inicio de sesión es válido y genuino , lo que significa que pasa por alto toda la seguridad que Python Social Auth le brinda.
Mobile App -> Facebook SDK : Opens the authorization prompt
Facebook SDK -> Mobile App : Gets the Facebook token
Mobile App -> Your API : Sends the Facebook token for authorization
Your API -> Django Login : Tries to validate the token
Django Login -> Your API : Returns a matching user
Your API -> Mobile App : Sends back an OAuth token for the user
Notarás que esto omite tu API durante la fase de autenticación y luego obliga a tu API a hacer suposiciones sobre el token que se transfiere. Pero definitivamente hay casos en los que este riesgo puede valer la pena , por lo que debes evaluarlo antes. tirándolo. Es un intercambio entre los inicios de sesión rápidos y nativos para su usuario y potencialmente el manejo de tokens malos o maliciosos .
Lo resolví usando tu opción A.
Lo que hago es registrar usuarios que usan un tercero para registrarse con su token de acceso de terceros.
url(r''^register-by-token/(?P<backend>[^/]+)/$'',
views.register_by_access_token),
De esta manera, puedo emitir una solicitud GET como esta:
GET http://localhost:8000/register-by-token/facebook/?access_token=123456
Y register_by_access_token
es llamado. request.backend.do_auth
consultará al proveedor la información del usuario del token y mágicamente registrará una cuenta de usuario con la información o iniciará sesión en el usuario si ya está registrado.
Luego, creo un token de forma manual y lo devuelvo como JSON para permitir que el cliente consulte mi API.
from oauthlib.common import generate_token
...
@psa(''social:complete'')
def register_by_access_token(request, backend):
# This view expects an access_token GET parameter, if it''s needed,
# request.backend and request.strategy will be loaded with the current
# backend and strategy.
third_party_token = request.GET.get(''access_token'')
user = request.backend.do_auth(third_party_token)
if user:
login(request, user)
# We get our app!
app = Application.objects.get(name="myapp")
# We delete the old token
try:
old = AccessToken.objects.get(user=user, application=app)
except:
pass
else:
old.delete()
# We create a new one
my_token = generate_token()
# We create the access token
# (we could create a refresh token too the same way)
AccessToken.objects.create(user=user,
application=app,
expires=now() + timedelta(days=365),
token=my_token)
return "OK" # you can return your token as JSON here
else:
return "ERROR"
Simplemente no estoy seguro de la forma en que genero el token, ¿es esta una buena práctica? ¡Bueno, mientras tanto, funciona!
Tal vez django-rest-framework-social-oauth2 es lo que estás buscando. Este paquete depende de python-social-auth
y django-oauth-toolkit
, que ya usa. Escanee rápidamente la documentación y parece implementar solo lo que intenta hacer.