social_core - ¿Se necesitan sesiones para python-social-auth?
social_core django (2)
No, no es necesario usar sesiones (sistema de inicio de sesión estándar de Django) con python-social-auth. Lo que necesita para que JWT y PSA trabajen juntos es DRF.
Aquí está mi solución:
Usé la url estándar de PSA para hacer que la solicitud fuera demasiado social /login/(?P<backend>[^/]+)/$
, cambié la url en urls.py para hacer coincidir la redirección de Facebook / Twitter a la mía.
url(r''^complete/(?P<backend>[^/]+)/$'', views.SocialAuthViewComplete.as_view()),
El punto de usar la API es tener acceso a los datos del usuario en la solicitud de PSA. DRF le permite hacerlo si tiene autenticación JWT en DEFAULT_AUTHENTICATION_CLASSES
REST_FRAMEWORK = {
''DEFAULT_AUTHENTICATION_CLASSES'': (
''rest_framework.authentication.SessionAuthentication'',
''rest_framework.authentication.TokenAuthentication'',
''rest_framework_jwt.authentication.JSONWebTokenAuthentication'',
),}
En views.py
from social.apps.django_app.views import complete
class SocialAuthViewComplete(APIView):
permission_classes = ()
def post(self, request, backend, *args, **kwargs):
try:
#Wrap up PSA''s `complete` method.
authentication = complete(request, backend, *args, **kwargs)
except Exception, e:
exc = {
''error'': str(e)
}
return Response(exc, status=status.HTTP_400_BAD_REQUEST)
return Response({''data'': authentication}, status=status.HTTP_202_ACCEPTED)
Luego modifiqué el método do_complete
en PSA:
def do_complete(backend, login, user=None, redirect_name=''next'',
*args, **kwargs):
# pop redirect value before the session is trashed on login()
data = backend.strategy.request_data()
redirect_value = backend.strategy.session_get(redirect_name, '''') or /
data.get(redirect_name, '''')
is_authenticated = user_is_authenticated(user)
user = is_authenticated and user or None
partial = partial_pipeline_data(backend, user, *args, **kwargs)
if partial:
xargs, xkwargs = partial
user = backend.continue_pipeline(*xargs, **xkwargs)
else:
user = backend.complete(user=user, *args, **kwargs)
user_model = backend.strategy.storage.user.user_model()
if user and not isinstance(user, user_model):
return user
if is_authenticated:
if not user:
information = ''setting_url(backend, redirect_value, LOGIN_REDIRECT_URL''
else:
information = ''setting_url(backend, redirect_value, NEW_ASSOCIATION_REDIRECT_URL,LOGIN_REDIRECT_URL''
elif user:
# Get the JWT payload for the user.
payload = jwt_payload_handler(user)
if user_is_active(user):
is_new = getattr(user, ''is_new'', False)
if is_new:
information = ''setting_url(backend, NEW_USER_REDIRECT_URL, redirect_value, LOGIN_REDIRECT_URL''
else:
information = ''setting_url(backend, redirect_value, LOGIN_REDIRECT_URL''
else:
return Response({
''status'': ''Unauthorized'',
''message'': ''The user account is disabled.''
}, status=status.HTTP_401_UNAUTHORIZED)
else:
information = ''setting_url(backend, LOGIN_ERROR_URL, LOGIN_URL''
return { ''an information i may use in future'': information,
''token'': jwt_encode_handler(payload) # Create the response object with the JWT payload.
}
Probé tuberías y asociación de usuarios y funciona correctamente. También siempre puede modificar otro método de PSA, si lo necesita para trabajar con JWT.
Estoy creando una aplicación django con un backend API (construido con DRF) y un cliente angularjs. Mi objetivo es desacoplar completamente el servidor y el cliente utilizando JWT en lugar de sesiones. Estoy intentando integrar python-social-auth (PSA) con django-rest-framework-jwt (DRFJWT), así que mi objetivo es tener un flujo de autenticación de algo para esto:
Los registros de usuario con correo electrónico / facebook a través del cliente angular -> las publicaciones del cliente forman la url de PSA -> inicio de sesión PSA / crear usuario -> [!] DRFJWT crea un token que luego envía al cliente -> el token del almacén del cliente en el almacenamiento local y luego usa el token cada solicitud
[!]: Aquí es donde estoy luchando. Mi pensamiento es que puedo modificar el método do_complete en PSA como tal
from rest_framework_jwt.utils import jwt_payload_handler, jwt_encode_handler
def do_complete(backend, login, user=None, redirect_name=''next'',
*args, **kwargs):
# pop redirect value before the session is trashed on login()
data = backend.strategy.request_data()
redirect_value = backend.strategy.session_get(redirect_name, '''') or /
data.get(redirect_name, '''')
is_authenticated = user_is_authenticated(user)
user = is_authenticated and user or None
partial = partial_pipeline_data(backend, user, *args, **kwargs)
if partial:
xargs, xkwargs = partial
user = backend.continue_pipeline(*xargs, **xkwargs)
else:
user = backend.complete(user=user, *args, **kwargs)
if user_is_active(user):
# catch is_new/social_user in case login() resets the instance
is_new = getattr(user, ''is_new'', False)
social_user = user.social_user
login(backend, user, social_user)
payload = jwt_payload_handler(user)
return { ''token'': jwt_encode_handler(payload) }
¿Es esta la única manera de hacer lo que estoy tratando de lograr?
También me pregunto si está bien, desde el punto de vista de las mejores prácticas, utilizar sesiones para administrar el canal y JWT para la autenticación.
También estoy usando python-social-auth y django-rest-framework-jwt para la autenticación de usuarios.
La forma en que pude integrar los dos sistemas de autenticación juntos fue mediante la creación de una vista personalizada que toma el '' access_token '' proporcionado por el proveedor de oAuth e intenta crear un nuevo usuario con él. Una vez que se crea el usuario, en lugar de devolver el usuario / sesión autenticado, devuelvo el token JWT.
Los siguientes fragmentos de código explican la solución.
Back-End
En mi archivo views.py incluí lo siguiente:
@psa()
def auth_by_token(request, backend):
"""Decorator that creates/authenticates a user with an access_token"""
token = request.DATA.get(''access_token'')
user = request.user
user = request.backend.do_auth(
access_token=request.DATA.get(''access_token'')
)
if user:
return user
else:
return None
class FacebookView(views.APIView):
"""View to authenticate users through Facebook."""
permission_classes = (permissions.AllowAny,)
def post(self, request, format=None):
auth_token = request.DATA.get(''access_token'', None)
backend = request.DATA.get(''backend'', None)
if auth_token and backend:
try:
# Try to authenticate the user using python-social-auth
user = auth_by_token(request, backend)
except Exception,e:
return Response({
''status'': ''Bad request'',
''message'': ''Could not authenticate with the provided token.''
}, status=status.HTTP_400_BAD_REQUEST)
if user:
if not user.is_active:
return Response({
''status'': ''Unauthorized'',
''message'': ''The user account is disabled.''
}, status=status.HTTP_401_UNAUTHORIZED)
# This is the part that differs from the normal python-social-auth implementation.
# Return the JWT instead.
# Get the JWT payload for the user.
payload = jwt_payload_handler(user)
# Include original issued at time for a brand new token,
# to allow token refresh
if api_settings.JWT_ALLOW_REFRESH:
payload[''orig_iat''] = timegm(
datetime.utcnow().utctimetuple()
)
# Create the response object with the JWT payload.
response_data = {
''token'': jwt_encode_handler(payload)
}
return Response(response_data)
else:
return Response({
''status'': ''Bad request'',
''message'': ''Authentication could not be performed with received data.''
}, status=status.HTTP_400_BAD_REQUEST)
En mis urls.py he incluido la siguiente ruta:
urlpatterns = patterns('''',
...
url(r''^api/v1/auth/facebook/'', FacebookView.as_view()),
...
)
Interfaz
Ahora que la autenticación de back-end está conectada, puede usar cualquier biblioteca de frontend para enviar el símbolo de acceso y autenticar al usuario. En mi caso utilicé AngularJS .
En un archivo de controlador llamo a la API así:
/**
* This function gets called after successfully getting the access_token from Facebook''s API.
*/
function successLoginFbFn(response) {
var deferred = $q.defer();
$http.post(''/api/v1/auth/facebook/'', {
"access_token": response.authResponse.accessToken,
"backend": "facebook"
}).success(function(response, status, headers, config) {
// Success
if (response.token) {
// Save the token to localStorage and redirect the user to the front-page.
Authentication.setToken(response.token);
window.location = ''/'';
}
deferred.resolve(response, status, headers, config);
}).error(function(response, status, headers, config) {
// Error
console.error(''Authentication error.'');
deferred.reject(response, status, headers, config);
});
}
Con este enfoque puedes mezclar los dos complementos. Todos los tokens enviados provendrán de django-rest-framework-jwt a pesar de que los usuarios todavía pueden autenticarse con los proporcionados por sitios como Facebook, Google, Twitter, etc.
Solo mostré el enfoque de autenticación a través de Facebook, sin embargo, puede seguir un enfoque similar para otros proveedores.