mirror of
https://github.com/Tivix/django-rest-auth.git
synced 2024-11-22 09:06:40 +03:00
Merge pull request #387 from aleksihakli/master
Implement connect social accounts functionality
This commit is contained in:
commit
41ae498be0
|
@ -111,11 +111,15 @@ Facebook
|
|||
.. code-block:: python
|
||||
|
||||
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
|
||||
from rest_auth.registration.views import SocialLoginView
|
||||
from rest_auth.registration.views import SocialLoginView, SocialConnectView
|
||||
|
||||
class FacebookLogin(SocialLoginView):
|
||||
adapter_class = FacebookOAuth2Adapter
|
||||
|
||||
# Add a connect view if you want to allow connecting existing accounts
|
||||
class FacebookConnect(SocialConnectView):
|
||||
adapter_class = FacebookOAuth2Adapter
|
||||
|
||||
4. Create url for FacebookLogin view:
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -123,6 +127,7 @@ Facebook
|
|||
urlpatterns += [
|
||||
...,
|
||||
url(r'^rest-auth/facebook/$', FacebookLogin.as_view(), name='fb_login')
|
||||
url(r'^rest-auth/facebook/connect/$', FacebookConnect.as_view(), name='fb_connect')
|
||||
]
|
||||
|
||||
|
||||
|
@ -136,13 +141,19 @@ If you are using Twitter for your social authentication, it is a bit different s
|
|||
.. code-block:: python
|
||||
|
||||
from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter
|
||||
from rest_auth.views import LoginView
|
||||
from rest_auth.social_serializers import TwitterLoginSerializer
|
||||
from rest_auth.registration.views import SocialLoginView
|
||||
from rest_auth.social_serializers import TwitterLoginSerializer, TwitterConnectSerializer
|
||||
|
||||
class TwitterLogin(LoginView):
|
||||
class TwitterLogin(SocialLoginView):
|
||||
serializer_class = TwitterLoginSerializer
|
||||
adapter_class = TwitterOAuthAdapter
|
||||
|
||||
# Add a connect view if you want to allow connecting existing accounts
|
||||
class TwitterConnect(SocialConnectView):
|
||||
serializer_class = TwitterConnectSerializer
|
||||
adapter_class = TwitterOAuthAdapter
|
||||
|
||||
|
||||
4. Create url for TwitterLogin view:
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -150,6 +161,7 @@ If you are using Twitter for your social authentication, it is a bit different s
|
|||
urlpatterns += [
|
||||
...,
|
||||
url(r'^rest-auth/twitter/$', TwitterLogin.as_view(), name='twitter_login')
|
||||
url(r'^rest-auth/twitter/connect/$', TwitterConnect.as_view(), name='twitter_login')
|
||||
]
|
||||
.. note:: Starting from v0.21.0, django-allauth has dropped support for context processors. Check out http://django-allauth.readthedocs.org/en/latest/changelog.html#from-0-21-0 for more details.
|
||||
|
||||
|
|
|
@ -14,10 +14,6 @@ except ImportError:
|
|||
|
||||
from rest_framework import serializers
|
||||
from requests.exceptions import HTTPError
|
||||
# Import is needed only if we are using social login, in which
|
||||
# case the allauth.socialaccount will be declared
|
||||
if 'allauth.socialaccount' in settings.INSTALLED_APPS:
|
||||
from allauth.socialaccount.helpers import complete_social_login
|
||||
|
||||
|
||||
class SocialLoginSerializer(serializers.Serializer):
|
||||
|
@ -186,3 +182,44 @@ class RegisterSerializer(serializers.Serializer):
|
|||
|
||||
class VerifyEmailSerializer(serializers.Serializer):
|
||||
key = serializers.CharField()
|
||||
|
||||
|
||||
# Import is needed only if we are using social login, in which
|
||||
# case the allauth.socialaccount will be declared
|
||||
if 'allauth.socialaccount' in settings.INSTALLED_APPS:
|
||||
from allauth.socialaccount.helpers import complete_social_login
|
||||
from allauth.socialaccount.models import SocialAccount
|
||||
from allauth.socialaccount.providers.base import AuthProcess
|
||||
|
||||
class SocialAccountSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
serialize allauth SocialAccounts for use with a REST API
|
||||
"""
|
||||
class Meta:
|
||||
model = SocialAccount
|
||||
fields = (
|
||||
'id',
|
||||
'provider',
|
||||
'uid',
|
||||
'last_login',
|
||||
'date_joined',
|
||||
'extra_data',
|
||||
)
|
||||
|
||||
|
||||
class SocialConnectMixin(object):
|
||||
def get_social_login(self, *args, **kwargs):
|
||||
"""
|
||||
set the social login process state to connect rather than login
|
||||
|
||||
Refer to the implementation of get_social_login in base class and to the
|
||||
allauth.socialaccount.helpers module complete_social_login function.
|
||||
"""
|
||||
|
||||
social_login = super(SocialConnectMixin, self).get_social_login(*args, **kwargs)
|
||||
social_login.state['process'] = AuthProcess.CONNECT
|
||||
return social_login
|
||||
|
||||
|
||||
class SocialConnectSerializer(SocialConnectMixin, SocialLoginSerializer):
|
||||
pass
|
||||
|
|
|
@ -5,11 +5,13 @@ from django.views.decorators.debug import sensitive_post_parameters
|
|||
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.permissions import (AllowAny,
|
||||
IsAuthenticated)
|
||||
from rest_framework.decorators import detail_route
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
from rest_framework.generics import CreateAPIView
|
||||
from rest_framework import status
|
||||
|
||||
from allauth.account.adapter import get_adapter
|
||||
from allauth.account.views import ConfirmEmailView
|
||||
from allauth.account.utils import complete_signup
|
||||
from allauth.account import app_settings as allauth_settings
|
||||
|
@ -19,7 +21,8 @@ from rest_auth.app_settings import (TokenSerializer,
|
|||
create_token)
|
||||
from rest_auth.models import TokenModel
|
||||
from rest_auth.registration.serializers import (SocialLoginSerializer,
|
||||
VerifyEmailSerializer)
|
||||
VerifyEmailSerializer,
|
||||
SocialConnectSerializer)
|
||||
from rest_auth.utils import jwt_encode
|
||||
from rest_auth.views import LoginView
|
||||
from .app_settings import RegisterSerializer, register_permission_classes
|
||||
|
@ -91,31 +94,98 @@ class VerifyEmailView(APIView, ConfirmEmailView):
|
|||
return Response({'detail': _('ok')}, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class SocialLoginView(LoginView):
|
||||
"""
|
||||
class used for social authentications
|
||||
example usage for facebook with access_token
|
||||
-------------
|
||||
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
|
||||
if 'allauth.socialaccount' in settings.INSTALLED_APPS:
|
||||
from allauth.socialaccount import signals
|
||||
from allauth.socialaccount.models import SocialAccount
|
||||
from allauth.socialaccount.adapter import get_adapter
|
||||
|
||||
class FacebookLogin(SocialLoginView):
|
||||
adapter_class = FacebookOAuth2Adapter
|
||||
-------------
|
||||
from rest_auth.registration.serializers import SocialAccountSerializer
|
||||
|
||||
example usage for facebook with code
|
||||
|
||||
-------------
|
||||
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
|
||||
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
|
||||
class SocialLoginView(LoginView):
|
||||
"""
|
||||
class used for social authentications
|
||||
example usage for facebook with access_token
|
||||
-------------
|
||||
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
|
||||
|
||||
class FacebookLogin(SocialLoginView):
|
||||
adapter_class = FacebookOAuth2Adapter
|
||||
client_class = OAuth2Client
|
||||
callback_url = 'localhost:8000'
|
||||
-------------
|
||||
"""
|
||||
class FacebookLogin(SocialLoginView):
|
||||
adapter_class = FacebookOAuth2Adapter
|
||||
-------------
|
||||
|
||||
serializer_class = SocialLoginSerializer
|
||||
example usage for facebook with code
|
||||
|
||||
def process_login(self):
|
||||
get_adapter(self.request).login(self.request, self.user)
|
||||
-------------
|
||||
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
|
||||
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
|
||||
|
||||
class FacebookLogin(SocialLoginView):
|
||||
adapter_class = FacebookOAuth2Adapter
|
||||
client_class = OAuth2Client
|
||||
callback_url = 'localhost:8000'
|
||||
-------------
|
||||
"""
|
||||
|
||||
serializer_class = SocialLoginSerializer
|
||||
|
||||
def process_login(self):
|
||||
get_adapter(self.request).login(self.request, self.user)
|
||||
|
||||
|
||||
class SocialConnectView(SocialLoginView):
|
||||
"""
|
||||
class used for social account linking
|
||||
|
||||
example usage for facebook with access_token
|
||||
-------------
|
||||
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
|
||||
|
||||
class FacebookConnect(SocialConnectView):
|
||||
adapter_class = FacebookOAuth2Adapter
|
||||
-------------
|
||||
"""
|
||||
|
||||
serializer_class = SocialConnectSerializer
|
||||
permission_classes = (IsAuthenticated,)
|
||||
|
||||
|
||||
class SocialAccountViewSet(GenericViewSet):
|
||||
"""
|
||||
allauth SocialAccount REST API read and disconnect views for logged in users
|
||||
|
||||
Refer to the django-allauth package implementation of the models and
|
||||
account handling logic for more details, this viewset emulates the allauth web UI.
|
||||
"""
|
||||
|
||||
serializer_class = SocialAccountSerializer
|
||||
permission_classes = (IsAuthenticated,)
|
||||
queryset = SocialAccount.objects.none()
|
||||
|
||||
def get_queryset(self):
|
||||
return SocialAccount.objects.filter(user=self.request.user)
|
||||
|
||||
def list(self, request):
|
||||
"""
|
||||
list SocialAccounts for the currently logged in user
|
||||
"""
|
||||
|
||||
return Response(self.get_serializer(self.get_queryset(), many=True).data)
|
||||
|
||||
@detail_route(methods=['POST'])
|
||||
def disconnect(self, request, pk):
|
||||
"""
|
||||
disconnect SocialAccount from remote service for the currently logged in user
|
||||
"""
|
||||
|
||||
accounts = self.get_queryset()
|
||||
account = accounts.get(pk=pk)
|
||||
get_adapter(self.request).validate_disconnect(account, accounts)
|
||||
|
||||
account.delete()
|
||||
signals.social_account_removed.send(
|
||||
sender=SocialAccount,
|
||||
request=self.request,
|
||||
socialaccount=account
|
||||
)
|
||||
|
||||
return Response(self.get_serializer(account).data)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from django.conf import settings
|
||||
from django.http import HttpRequest
|
||||
from rest_framework import serializers
|
||||
|
||||
# Import is needed only if we are using social login, in which
|
||||
# case the allauth.socialaccount will be declared
|
||||
if 'allauth.socialaccount' in settings.INSTALLED_APPS:
|
||||
|
@ -8,68 +9,74 @@ if 'allauth.socialaccount' in settings.INSTALLED_APPS:
|
|||
from allauth.socialaccount.models import SocialToken
|
||||
from allauth.socialaccount.providers.oauth.client import OAuthError
|
||||
|
||||
from rest_auth.registration.serializers import SocialConnectMixin
|
||||
|
||||
class TwitterLoginSerializer(serializers.Serializer):
|
||||
access_token = serializers.CharField()
|
||||
token_secret = serializers.CharField()
|
||||
|
||||
def _get_request(self):
|
||||
request = self.context.get('request')
|
||||
if not isinstance(request, HttpRequest):
|
||||
request = request._request
|
||||
return request
|
||||
class TwitterLoginSerializer(serializers.Serializer):
|
||||
access_token = serializers.CharField()
|
||||
token_secret = serializers.CharField()
|
||||
|
||||
def get_social_login(self, adapter, app, token, response):
|
||||
"""
|
||||
:param adapter: allauth.socialaccount Adapter subclass.
|
||||
Usually OAuthAdapter or Auth2Adapter
|
||||
:param app: `allauth.socialaccount.SocialApp` instance
|
||||
:param token: `allauth.socialaccount.SocialToken` instance
|
||||
:param response: Provider's response for OAuth1. Not used in the
|
||||
:returns: A populated instance of the
|
||||
`allauth.socialaccount.SocialLoginView` instance
|
||||
"""
|
||||
request = self._get_request()
|
||||
social_login = adapter.complete_login(request, app, token,
|
||||
response=response)
|
||||
social_login.token = token
|
||||
return social_login
|
||||
def _get_request(self):
|
||||
request = self.context.get('request')
|
||||
if not isinstance(request, HttpRequest):
|
||||
request = request._request
|
||||
return request
|
||||
|
||||
def validate(self, attrs):
|
||||
view = self.context.get('view')
|
||||
request = self._get_request()
|
||||
def get_social_login(self, adapter, app, token, response):
|
||||
"""
|
||||
:param adapter: allauth.socialaccount Adapter subclass.
|
||||
Usually OAuthAdapter or Auth2Adapter
|
||||
:param app: `allauth.socialaccount.SocialApp` instance
|
||||
:param token: `allauth.socialaccount.SocialToken` instance
|
||||
:param response: Provider's response for OAuth1. Not used in the
|
||||
:returns: A populated instance of the
|
||||
`allauth.socialaccount.SocialLoginView` instance
|
||||
"""
|
||||
request = self._get_request()
|
||||
social_login = adapter.complete_login(request, app, token,
|
||||
response=response)
|
||||
social_login.token = token
|
||||
return social_login
|
||||
|
||||
if not view:
|
||||
raise serializers.ValidationError(
|
||||
"View is not defined, pass it as a context variable"
|
||||
)
|
||||
def validate(self, attrs):
|
||||
view = self.context.get('view')
|
||||
request = self._get_request()
|
||||
|
||||
adapter_class = getattr(view, 'adapter_class', None)
|
||||
if not adapter_class:
|
||||
raise serializers.ValidationError("Define adapter_class in view")
|
||||
if not view:
|
||||
raise serializers.ValidationError(
|
||||
"View is not defined, pass it as a context variable"
|
||||
)
|
||||
|
||||
adapter = adapter_class(request)
|
||||
app = adapter.get_provider().get_app(request)
|
||||
adapter_class = getattr(view, 'adapter_class', None)
|
||||
if not adapter_class:
|
||||
raise serializers.ValidationError("Define adapter_class in view")
|
||||
|
||||
access_token = attrs.get('access_token')
|
||||
token_secret = attrs.get('token_secret')
|
||||
adapter = adapter_class(request)
|
||||
app = adapter.get_provider().get_app(request)
|
||||
|
||||
request.session['oauth_api.twitter.com_access_token'] = {
|
||||
'oauth_token': access_token,
|
||||
'oauth_token_secret': token_secret,
|
||||
}
|
||||
token = SocialToken(token=access_token, token_secret=token_secret)
|
||||
token.app = app
|
||||
access_token = attrs.get('access_token')
|
||||
token_secret = attrs.get('token_secret')
|
||||
|
||||
try:
|
||||
login = self.get_social_login(adapter, app, token, access_token)
|
||||
complete_social_login(request, login)
|
||||
except OAuthError as e:
|
||||
raise serializers.ValidationError(str(e))
|
||||
request.session['oauth_api.twitter.com_access_token'] = {
|
||||
'oauth_token': access_token,
|
||||
'oauth_token_secret': token_secret,
|
||||
}
|
||||
token = SocialToken(token=access_token, token_secret=token_secret)
|
||||
token.app = app
|
||||
|
||||
if not login.is_existing:
|
||||
login.lookup()
|
||||
login.save(request, connect=True)
|
||||
attrs['user'] = login.account.user
|
||||
try:
|
||||
login = self.get_social_login(adapter, app, token, access_token)
|
||||
complete_social_login(request, login)
|
||||
except OAuthError as e:
|
||||
raise serializers.ValidationError(str(e))
|
||||
|
||||
return attrs
|
||||
if not login.is_existing:
|
||||
login.lookup()
|
||||
login.save(request, connect=True)
|
||||
attrs['user'] = login.account.user
|
||||
|
||||
return attrs
|
||||
|
||||
|
||||
class TwitterConnectSerializer(SocialConnectMixin, TwitterLoginSerializer):
|
||||
pass
|
||||
|
|
Loading…
Reference in New Issue
Block a user