Skip to content

Commit 41ae498

Browse files
authored
Merge pull request #387 from aleksihakli/master
Implement connect social accounts functionality
2 parents a892ca3 + 8a4afe7 commit 41ae498

File tree

4 files changed

+223
-97
lines changed

4 files changed

+223
-97
lines changed

docs/installation.rst

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,18 +111,23 @@ Facebook
111111
.. code-block:: python
112112
113113
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
114-
from rest_auth.registration.views import SocialLoginView
114+
from rest_auth.registration.views import SocialLoginView, SocialConnectView
115115
116116
class FacebookLogin(SocialLoginView):
117117
adapter_class = FacebookOAuth2Adapter
118118
119+
# Add a connect view if you want to allow connecting existing accounts
120+
class FacebookConnect(SocialConnectView):
121+
adapter_class = FacebookOAuth2Adapter
122+
119123
4. Create url for FacebookLogin view:
120124

121125
.. code-block:: python
122126
123127
urlpatterns += [
124128
...,
125129
url(r'^rest-auth/facebook/$', FacebookLogin.as_view(), name='fb_login')
130+
url(r'^rest-auth/facebook/connect/$', FacebookConnect.as_view(), name='fb_connect')
126131
]
127132
128133
@@ -136,20 +141,27 @@ If you are using Twitter for your social authentication, it is a bit different s
136141
.. code-block:: python
137142
138143
from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter
139-
from rest_auth.views import LoginView
140-
from rest_auth.social_serializers import TwitterLoginSerializer
144+
from rest_auth.registration.views import SocialLoginView
145+
from rest_auth.social_serializers import TwitterLoginSerializer, TwitterConnectSerializer
141146
142-
class TwitterLogin(LoginView):
147+
class TwitterLogin(SocialLoginView):
143148
serializer_class = TwitterLoginSerializer
144149
adapter_class = TwitterOAuthAdapter
145150
151+
# Add a connect view if you want to allow connecting existing accounts
152+
class TwitterConnect(SocialConnectView):
153+
serializer_class = TwitterConnectSerializer
154+
adapter_class = TwitterOAuthAdapter
155+
156+
146157
4. Create url for TwitterLogin view:
147158

148159
.. code-block:: python
149160
150161
urlpatterns += [
151162
...,
152163
url(r'^rest-auth/twitter/$', TwitterLogin.as_view(), name='twitter_login')
164+
url(r'^rest-auth/twitter/connect/$', TwitterConnect.as_view(), name='twitter_login')
153165
]
154166
.. 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.
155167

rest_auth/registration/serializers.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@
1414

1515
from rest_framework import serializers
1616
from requests.exceptions import HTTPError
17-
# Import is needed only if we are using social login, in which
18-
# case the allauth.socialaccount will be declared
19-
if 'allauth.socialaccount' in settings.INSTALLED_APPS:
20-
from allauth.socialaccount.helpers import complete_social_login
2117

2218

2319
class SocialLoginSerializer(serializers.Serializer):
@@ -186,3 +182,44 @@ def save(self, request):
186182

187183
class VerifyEmailSerializer(serializers.Serializer):
188184
key = serializers.CharField()
185+
186+
187+
# Import is needed only if we are using social login, in which
188+
# case the allauth.socialaccount will be declared
189+
if 'allauth.socialaccount' in settings.INSTALLED_APPS:
190+
from allauth.socialaccount.helpers import complete_social_login
191+
from allauth.socialaccount.models import SocialAccount
192+
from allauth.socialaccount.providers.base import AuthProcess
193+
194+
class SocialAccountSerializer(serializers.ModelSerializer):
195+
"""
196+
serialize allauth SocialAccounts for use with a REST API
197+
"""
198+
class Meta:
199+
model = SocialAccount
200+
fields = (
201+
'id',
202+
'provider',
203+
'uid',
204+
'last_login',
205+
'date_joined',
206+
'extra_data',
207+
)
208+
209+
210+
class SocialConnectMixin(object):
211+
def get_social_login(self, *args, **kwargs):
212+
"""
213+
set the social login process state to connect rather than login
214+
215+
Refer to the implementation of get_social_login in base class and to the
216+
allauth.socialaccount.helpers module complete_social_login function.
217+
"""
218+
219+
social_login = super(SocialConnectMixin, self).get_social_login(*args, **kwargs)
220+
social_login.state['process'] = AuthProcess.CONNECT
221+
return social_login
222+
223+
224+
class SocialConnectSerializer(SocialConnectMixin, SocialLoginSerializer):
225+
pass

rest_auth/registration/views.py

Lines changed: 95 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55

66
from rest_framework.views import APIView
77
from rest_framework.response import Response
8-
from rest_framework.permissions import AllowAny
8+
from rest_framework.permissions import (AllowAny,
9+
IsAuthenticated)
10+
from rest_framework.decorators import detail_route
11+
from rest_framework.viewsets import GenericViewSet
912
from rest_framework.generics import CreateAPIView
1013
from rest_framework import status
1114

12-
from allauth.account.adapter import get_adapter
1315
from allauth.account.views import ConfirmEmailView
1416
from allauth.account.utils import complete_signup
1517
from allauth.account import app_settings as allauth_settings
@@ -19,7 +21,8 @@
1921
create_token)
2022
from rest_auth.models import TokenModel
2123
from rest_auth.registration.serializers import (SocialLoginSerializer,
22-
VerifyEmailSerializer)
24+
VerifyEmailSerializer,
25+
SocialConnectSerializer)
2326
from rest_auth.utils import jwt_encode
2427
from rest_auth.views import LoginView
2528
from .app_settings import RegisterSerializer, register_permission_classes
@@ -91,31 +94,98 @@ def post(self, request, *args, **kwargs):
9194
return Response({'detail': _('ok')}, status=status.HTTP_200_OK)
9295

9396

94-
class SocialLoginView(LoginView):
95-
"""
96-
class used for social authentications
97-
example usage for facebook with access_token
98-
-------------
99-
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
97+
if 'allauth.socialaccount' in settings.INSTALLED_APPS:
98+
from allauth.socialaccount import signals
99+
from allauth.socialaccount.models import SocialAccount
100+
from allauth.socialaccount.adapter import get_adapter
100101

101-
class FacebookLogin(SocialLoginView):
102-
adapter_class = FacebookOAuth2Adapter
103-
-------------
102+
from rest_auth.registration.serializers import SocialAccountSerializer
104103

105-
example usage for facebook with code
106104

107-
-------------
108-
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
109-
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
105+
class SocialLoginView(LoginView):
106+
"""
107+
class used for social authentications
108+
example usage for facebook with access_token
109+
-------------
110+
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
110111
111-
class FacebookLogin(SocialLoginView):
112-
adapter_class = FacebookOAuth2Adapter
113-
client_class = OAuth2Client
114-
callback_url = 'localhost:8000'
115-
-------------
116-
"""
112+
class FacebookLogin(SocialLoginView):
113+
adapter_class = FacebookOAuth2Adapter
114+
-------------
117115
118-
serializer_class = SocialLoginSerializer
116+
example usage for facebook with code
119117
120-
def process_login(self):
121-
get_adapter(self.request).login(self.request, self.user)
118+
-------------
119+
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
120+
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
121+
122+
class FacebookLogin(SocialLoginView):
123+
adapter_class = FacebookOAuth2Adapter
124+
client_class = OAuth2Client
125+
callback_url = 'localhost:8000'
126+
-------------
127+
"""
128+
129+
serializer_class = SocialLoginSerializer
130+
131+
def process_login(self):
132+
get_adapter(self.request).login(self.request, self.user)
133+
134+
135+
class SocialConnectView(SocialLoginView):
136+
"""
137+
class used for social account linking
138+
139+
example usage for facebook with access_token
140+
-------------
141+
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
142+
143+
class FacebookConnect(SocialConnectView):
144+
adapter_class = FacebookOAuth2Adapter
145+
-------------
146+
"""
147+
148+
serializer_class = SocialConnectSerializer
149+
permission_classes = (IsAuthenticated,)
150+
151+
152+
class SocialAccountViewSet(GenericViewSet):
153+
"""
154+
allauth SocialAccount REST API read and disconnect views for logged in users
155+
156+
Refer to the django-allauth package implementation of the models and
157+
account handling logic for more details, this viewset emulates the allauth web UI.
158+
"""
159+
160+
serializer_class = SocialAccountSerializer
161+
permission_classes = (IsAuthenticated,)
162+
queryset = SocialAccount.objects.none()
163+
164+
def get_queryset(self):
165+
return SocialAccount.objects.filter(user=self.request.user)
166+
167+
def list(self, request):
168+
"""
169+
list SocialAccounts for the currently logged in user
170+
"""
171+
172+
return Response(self.get_serializer(self.get_queryset(), many=True).data)
173+
174+
@detail_route(methods=['POST'])
175+
def disconnect(self, request, pk):
176+
"""
177+
disconnect SocialAccount from remote service for the currently logged in user
178+
"""
179+
180+
accounts = self.get_queryset()
181+
account = accounts.get(pk=pk)
182+
get_adapter(self.request).validate_disconnect(account, accounts)
183+
184+
account.delete()
185+
signals.social_account_removed.send(
186+
sender=SocialAccount,
187+
request=self.request,
188+
socialaccount=account
189+
)
190+
191+
return Response(self.get_serializer(account).data)

0 commit comments

Comments
 (0)