From ce1605845dfdbbf4a02a990d805952a7908ba25f Mon Sep 17 00:00:00 2001 From: yuri-0415 <63419338+yuri-0415@users.noreply.github.com> Date: Sun, 3 Jan 2021 15:37:38 +0900 Subject: [PATCH 01/11] Feature/#18 --- wadium/user/serializers.py | 29 ++++++++++++++++++++++------- wadium/user/views.py | 16 +++++++++++++--- wadium/wadium/adapters.py | 0 wadium/wadium/settings.py | 38 ++++++++++++++++++++++++++++++++++++++ wadium/wadium/urls.py | 1 + 5 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 wadium/wadium/adapters.py diff --git a/wadium/user/serializers.py b/wadium/user/serializers.py index 22371d8..a754f35 100644 --- a/wadium/user/serializers.py +++ b/wadium/user/serializers.py @@ -42,11 +42,11 @@ class Meta: ) extra_kwargs = {'username': {'required': False}} - def validate_auth_type(self, value): - if value in (self.OAUTH,): - raise serializers.ValidationError(f'{value} is not yet implemented.') - else: - return value + #def validate_auth_type(self, value): + # if value in (self.OAUTH,): + # raise serializers.ValidationError(f'{value} is not yet implemented.') + # else: + # return value def validate(self, data): if data['auth_type'] == self.TEST: @@ -73,6 +73,11 @@ def validate(self, data): missing = required - set(data.get('userprofile', {})) if missing: raise serializers.ValidationError(f'{missing} is required') + elif data['auth_type'] == self.OAUTH: + required = {'name', 'email'} + missing = required - set(data.get('userprofile', {})) + if missing: + raise serializers.ValidationError(f'{missing} is required') return data def create(self, validated_data): @@ -86,6 +91,9 @@ def create(self, validated_data): if userprofile['email'] != email_auth.email_address.email: raise serializers.ValidationError({'email': 'Email does not match.'}) user = UserProfile.create_user(validated_data['username'], userprofile, test_user=False) + elif auth_type == self.OAUTH: + user = UserProfile.create_user(**validated_data, test_user=False) + else: raise ValueError() Token.objects.create(user=user) @@ -140,6 +148,9 @@ def validate(self, data): raise serializers.ValidationError('email is required') elif data['req_type'] == self.LOGIN and 'access_token' not in data: raise serializers.ValidationError('access_token is required') + elif data['auth_type'] == self.OATUH: + if 'username' not in data: + raise serializers.ValidationError('username is required') return data def get_user(self, validated_data): @@ -169,6 +180,10 @@ def get_user(self, validated_data): email_auth = get_object_or_404(EmailAuth, token=validated_data['access_token']) email_auth.is_valid(must_be_email=True) return email_auth.email_address.user + elif auth_type == self.OAUTH: + oauth = get_object_or_404(EmailAuth, token=validated_data['access_token']) + oauth.is_valid(must_be_email=True) + return oauth.email_address.user else: raise NotImplementedError() @@ -192,8 +207,8 @@ def get_connection(self, user): return UserSocialSerializer(user, context=self.context).data class UserSocialSerializer(serializers.ModelSerializer): - #google = serializers.SerializerMethodField() - #facebook = serializers.SerializerMethodField() + # google = serializers.SerializerMethodField() + # facebook = serializers.SerializerMethodField() google = serializers.CharField(source='user.usergoogle.google_sub', required=False) facebook = serializers.CharField(source='user.userfacebook.facebook_sub', required=False) class Meta: diff --git a/wadium/user/views.py b/wadium/user/views.py index eff57eb..043e002 100644 --- a/wadium/user/views.py +++ b/wadium/user/views.py @@ -1,17 +1,22 @@ -from django.contrib.auth import login, logout +from django.contrib.auth import login, logout, authenticate from django.contrib.auth.models import User from django.db import transaction from django.utils import timezone from django.shortcuts import get_object_or_404 -from .serializers import UserSerializer, UserLoginSerializer +from .serializers import UserSerializer, UserLoginSerializer, UserSelfSerializer, SocialSerializer from .models import EmailAddress, EmailAuth, UserProfile from rest_framework import status, viewsets from rest_framework.response import Response from rest_framework.decorators import action -from rest_framework.permissions import IsAuthenticated +from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.authtoken.models import Token +import requests +from django.conf import settings +from google.auth.transport import requests +from google.oauth2 import id_token +#from rest_framework.decorators import permission_classes class UserViewSet(viewsets.GenericViewSet): queryset = User.objects.all() @@ -51,6 +56,8 @@ def create(self, request): }) elif data['req_type'] == UserSerializer.CREATE: user = serializer.save() + elif data['auth_type'] == UserSerializer.OAUTH: + pass else: return Response(status=status.HTTP_501_NOT_IMPLEMENTED) @@ -59,6 +66,7 @@ def create(self, request): data['token'] = user.auth_token.key return Response(data=data, status=status.HTTP_201_CREATED) + @action(detail=False, methods=['POST']) def login(self, request): login_serializer = UserLoginSerializer(data=request.data) @@ -82,6 +90,8 @@ def login(self, request): return Response(status=status.HTTP_503_SERVICE_UNAVAILABLE) elif data['req_type'] == UserLoginSerializer.LOGIN: user = login_serializer.get_user(data) + elif data['auth_type'] == UserLoginSerializer.OAUTH: + pass else: return Response(status=status.HTTP_501_NOT_IMPLEMENTED) diff --git a/wadium/wadium/adapters.py b/wadium/wadium/adapters.py new file mode 100644 index 0000000..e69de29 diff --git a/wadium/wadium/settings.py b/wadium/wadium/settings.py index 0c726ef..b335306 100644 --- a/wadium/wadium/settings.py +++ b/wadium/wadium/settings.py @@ -45,6 +45,17 @@ 'rest_framework.authtoken', 'user.apps.UserConfig', 'story.apps.StoryConfig', + 'django.contrib.sites', + + #allauth + 'allauth', + 'allauth.account', + 'allauth.socialaccount', + + #provider + 'allauth.socialaccount.providers.google', + 'allauth.socialaccount.providers.facebook', + ] MIDDLEWARE = [ @@ -141,3 +152,30 @@ # Mailjet API MAILJET_API_KEY = os.getenv('MAILJET_API_KEY') MAILJET_API_SECRET = os.getenv('MAILJET_API_SECRET') + +AUTHENTICATION_BACKENDS = ( + # Needed to login by username in Django admin, regardless of `allauth` + 'django.contrib.auth.backends.ModelBackend', + + # `allauth` specific authentication methods, such as login by e-mail + 'allauth.account.auth_backends.AuthenticationBackend', +) + +SOCIALACCOUNT_PROVIDERS = { + 'google': { + # For each OAuth based provider, either add a ``SocialApp`` + # (``socialaccount`` app) containing the required client + # credentials, or list them here: + 'APP': { + 'client_id': '547861684988-3stvt30tjr8uu0j4llqeh8qpblf0a8uv.apps.googleusercontent.com', + 'secret': 'd-dpDCtOtOlBcrOozo81TJwB', + 'key': '' + } + } +} + +SITE_ID = 2 +LOGIN_REDIRECT_URL = '/' + +SOCIALACCOUNT_EMAIL_REQUIRED = True +SOCIALACCOUNT_AUTO_SIGNUP = True diff --git a/wadium/wadium/urls.py b/wadium/wadium/urls.py index 398f388..4c6e18d 100644 --- a/wadium/wadium/urls.py +++ b/wadium/wadium/urls.py @@ -25,6 +25,7 @@ path('admin/', admin.site.urls), path('', include('user.urls')), path('', include('story.urls')), + path('accounts/', include('allauth.urls')), ] if settings.DEBUG_TOOLBAR: From 50261331976ba30b28c2a75ed9930acf76ba7d98 Mon Sep 17 00:00:00 2001 From: yuri-0415 <63419338+yuri-0415@users.noreply.github.com> Date: Sun, 3 Jan 2021 15:44:41 +0900 Subject: [PATCH 02/11] Feature/#18 --- requirements.txt | Bin 282 -> 1768 bytes wadium/requirements.txt | Bin 0 -> 1768 bytes wadium/user/views.py | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 wadium/requirements.txt diff --git a/requirements.txt b/requirements.txt index 26567c4e1496d309766dea8a674e3552c383d606..7743816c1ed219bd204bf5fabf139ce4aa250d5d 100644 GIT binary patch literal 1768 zcmZ`)%W|7Q5S(+W@+r6i$gbp&n@>6A;3}U5=;a830VhsAK1ug9yNe{dRFq`4ccve+ z>wkaOHrmb(HrU3t_7$JfZv4sZqkXUn@7MNX6EItRAA#@i-P_91E11-drnW1u7fy-Po(OU`-Tj-NkzM_JkTI)aqglaw?rovslf5JK`hP_OaH= zT@br-JQ-AAu(K@sR$>2e)=7Ds?__Et_GC~%jV=EE1%`wDh<(ZOx@*|^_CC&lk|IVmt*UMF^?~z%CIUo<=$)RxlQt|eV9{Eg2se{kle?*-Peox+06Tm6@e8q`f zIiqqrb(8MPZbD{2Px$oSrvkr_I+bb4&Uca#XTOQaa*^p0D-Xumg%-I;Qx8Wv^z4ut zyOHUwPrR7E$0&zpCc-mzK>ri;azEY3=X6`VJAmO6JmW-!ghL-@Dpf6f9_<;}OOJI% zL#Hmf$|Nf9sL2QumGp3a)BTYzd||4*Wlmrk9OgMzip$l^eQHpNb0&wFMarD$8j102 z@Vx4TaGkdY-T4m0H+!^)=uetc?oRX7coLkipOIING?KQ2F5zc~zC_c1>Yx+yi+xr5 z&)hZKX7Za!JVTYvs&t7nyD8O64Eu$dPVRGF*jHNVf#!gI-nsq}{9c`f&P&q6d%MOe zZ>Mx#bY6SBS>7zv$C}Bz_jWH&IhpC9BjNu98ExRPZUX9-VohMmayc79ydd8L65C)d acuLp)J9tlS3CA0kGg?ONh2P$~zy1Ry?g-)l literal 282 zcmYjMOLD^?4Bc}VG=S~yw97@XksT5YiNNior>{twnJo4G@^y<$E8+kHShWzhxJE>0 zoB``#Qp?$MpoTbt0i#YApSRNW76uibmku-^n{K6~+4GCDZ;q)py|Ulwr|=a~VkPEf z?A!+>nGT$aA$wT>)~MabbfKoZ5K$UoRQsde(~=@(ft-x8>h@^ol65GJ^7pf1k`6(C j|34WO)BTW;+AVGpkA&PjSL*Un?t@c`^Uzz|m#p{$VQW^0 diff --git a/wadium/requirements.txt b/wadium/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..7743816c1ed219bd204bf5fabf139ce4aa250d5d GIT binary patch literal 1768 zcmZ`)%W|7Q5S(+W@+r6i$gbp&n@>6A;3}U5=;a830VhsAK1ug9yNe{dRFq`4ccve+ z>wkaOHrmb(HrU3t_7$JfZv4sZqkXUn@7MNX6EItRAA#@i-P_91E11-drnW1u7fy-Po(OU`-Tj-NkzM_JkTI)aqglaw?rovslf5JK`hP_OaH= zT@br-JQ-AAu(K@sR$>2e)=7Ds?__Et_GC~%jV=EE1%`wDh<(ZOx@*|^_CC&lk|IVmt*UMF^?~z%CIUo<=$)RxlQt|eV9{Eg2se{kle?*-Peox+06Tm6@e8q`f zIiqqrb(8MPZbD{2Px$oSrvkr_I+bb4&Uca#XTOQaa*^p0D-Xumg%-I;Qx8Wv^z4ut zyOHUwPrR7E$0&zpCc-mzK>ri;azEY3=X6`VJAmO6JmW-!ghL-@Dpf6f9_<;}OOJI% zL#Hmf$|Nf9sL2QumGp3a)BTYzd||4*Wlmrk9OgMzip$l^eQHpNb0&wFMarD$8j102 z@Vx4TaGkdY-T4m0H+!^)=uetc?oRX7coLkipOIING?KQ2F5zc~zC_c1>Yx+yi+xr5 z&)hZKX7Za!JVTYvs&t7nyD8O64Eu$dPVRGF*jHNVf#!gI-nsq}{9c`f&P&q6d%MOe zZ>Mx#bY6SBS>7zv$C}Bz_jWH&IhpC9BjNu98ExRPZUX9-VohMmayc79ydd8L65C)d acuLp)J9tlS3CA0kGg?ONh2P$~zy1Ry?g-)l literal 0 HcmV?d00001 diff --git a/wadium/user/views.py b/wadium/user/views.py index 043e002..bacf2d3 100644 --- a/wadium/user/views.py +++ b/wadium/user/views.py @@ -3,7 +3,7 @@ from django.db import transaction from django.utils import timezone from django.shortcuts import get_object_or_404 -from .serializers import UserSerializer, UserLoginSerializer, UserSelfSerializer, SocialSerializer +from .serializers import UserSerializer, UserLoginSerializer, UserSelfSerializer from .models import EmailAddress, EmailAuth, UserProfile from rest_framework import status, viewsets From da1379a1762c869dee18b7cd042689bf19aa7fd3 Mon Sep 17 00:00:00 2001 From: Sanggyu Lee <8325289+gyusang@users.noreply.github.com> Date: Sun, 3 Jan 2021 16:58:42 +0900 Subject: [PATCH 03/11] Add google support --- wadium/wadium/adapters.py | 30 ++++++++++++++++++++++++++++++ wadium/wadium/settings.py | 8 +++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/wadium/wadium/adapters.py b/wadium/wadium/adapters.py index e69de29..7e4e27f 100644 --- a/wadium/wadium/adapters.py +++ b/wadium/wadium/adapters.py @@ -0,0 +1,30 @@ +from allauth.account.adapter import DefaultAccountAdapter +from allauth.socialaccount.adapter import DefaultSocialAccountAdapter +from user.models import UserProfile +from rest_framework.authtoken.models import Token + + +class WadiumSocialAccountAdapter(DefaultSocialAccountAdapter): + def populate_user(self, request, sociallogin, data): + # print(sociallogin) + # print(data) + # print(sociallogin.account.extra_data) + return super(WadiumSocialAccountAdapter, self).populate_user(request, sociallogin, data) + + def save_user(self, request, sociallogin, form=None): + print(type(sociallogin.account)) + breakpoint() + userprofile = { + 'email': sociallogin.account.extra_data['email'], + 'name': sociallogin.account.extra_data['name'], + 'profile_image': sociallogin.account.extra_data['picture'] + } + user = UserProfile.create_user( + UserProfile.get_unique_username(sociallogin.account.extra_data['email']), + userprofile) + Token.objects.create(user=user) + print(sociallogin.account.provider) + # TODO create usergoogle, userfacebook + sociallogin.user = user + sociallogin.save(request) + return user diff --git a/wadium/wadium/settings.py b/wadium/wadium/settings.py index b335306..19998c7 100644 --- a/wadium/wadium/settings.py +++ b/wadium/wadium/settings.py @@ -54,7 +54,7 @@ #provider 'allauth.socialaccount.providers.google', - 'allauth.socialaccount.providers.facebook', + # 'allauth.socialaccount.providers.facebook', ] @@ -174,8 +174,14 @@ } } +ACCOUNT_AUTHENTICATED_LOGIN_REDIRECTS = False +SOCIALACCOUNT_QUERY_EMAIL = True + SITE_ID = 2 LOGIN_REDIRECT_URL = '/' +ACCOUNT_EMAIL_REQUIRED = True SOCIALACCOUNT_EMAIL_REQUIRED = True SOCIALACCOUNT_AUTO_SIGNUP = True + +SOCIALACCOUNT_ADAPTER = "wadium.adapters.WadiumSocialAccountAdapter" \ No newline at end of file From ebedca39ececd32df87e0ed63cebc382c5dc0e50 Mon Sep 17 00:00:00 2001 From: Sanggyu Lee <8325289+gyusang@users.noreply.github.com> Date: Sun, 3 Jan 2021 23:32:05 +0900 Subject: [PATCH 04/11] Create custom google login --- wadium/user/providers/google/__init__.py | 0 wadium/user/providers/google/provider.py | 9 +++ wadium/user/providers/google/urls.py | 7 ++ wadium/user/providers/google/views.py | 98 ++++++++++++++++++++++++ wadium/wadium/adapters.py | 49 ++++++++---- wadium/wadium/settings.py | 13 ++-- 6 files changed, 157 insertions(+), 19 deletions(-) create mode 100644 wadium/user/providers/google/__init__.py create mode 100644 wadium/user/providers/google/provider.py create mode 100644 wadium/user/providers/google/urls.py create mode 100644 wadium/user/providers/google/views.py diff --git a/wadium/user/providers/google/__init__.py b/wadium/user/providers/google/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wadium/user/providers/google/provider.py b/wadium/user/providers/google/provider.py new file mode 100644 index 0000000..ff536bc --- /dev/null +++ b/wadium/user/providers/google/provider.py @@ -0,0 +1,9 @@ +from allauth.socialaccount.providers.google.provider import GoogleProvider + + +class GoogleProviderNoRedirect(GoogleProvider): + id = 'google_no_redirect' + name = 'Custom Google' + + +provider_classes = [GoogleProviderNoRedirect] diff --git a/wadium/user/providers/google/urls.py b/wadium/user/providers/google/urls.py new file mode 100644 index 0000000..ad3974d --- /dev/null +++ b/wadium/user/providers/google/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from .views import oauth2_callback, oauth2_login + +urlpatterns = [ + path("google/login/", oauth2_login, name='google_no_redirect_login'), + path("google/login/callback/", oauth2_callback, name='google_no_redirect_callback') +] diff --git a/wadium/user/providers/google/views.py b/wadium/user/providers/google/views.py new file mode 100644 index 0000000..fd3fca8 --- /dev/null +++ b/wadium/user/providers/google/views.py @@ -0,0 +1,98 @@ +from allauth.socialaccount.providers.google.views import ( + OAuth2CallbackView, + OAuth2LoginView, + GoogleOAuth2Adapter, +) +from django.http import HttpResponseRedirect +from django.conf import settings +from .provider import GoogleProviderNoRedirect +from user.serializers import UserSerializer +from rest_framework.authtoken.models import Token +from rest_framework.response import Response +from rest_framework.views import APIView +from rest_framework import status + + +class SocialLoginView(APIView): + def dispatch(self, request, *args, **kwargs): + self.args = args + self.kwargs = kwargs + request = self.initialize_request(request, *args, **kwargs) + self.request = request + self.headers = self.default_response_headers # deprecate? + + try: + self.initial(request, *args, **kwargs) + except Exception as exc: + pass + + def render(self, response): + query_string = self.request.META.get('QUERY_STRING') + if query_string: + queries = query_string.split('&') + for i in range(len(queries)): + if 'state' in queries[i]: + queries[i] = 'state=' + 'x' * 12 + elif 'code' in queries[i]: + queries[i] = 'code=' + 'x' * 75 + self.request.META['QUERY_STRING'] = '&'.join(queries) + self.response = self.finalize_response(self.request, response, *self.args, **self.kwargs) + return self.response + + +class TokenOAuth2CallbackView(OAuth2CallbackView): + def dispatch(self, request, *args, **kwargs): + api_view = SocialLoginView() + api_view.dispatch(request, *args, **kwargs) + res = super(TokenOAuth2CallbackView, self).dispatch(request, *args, **kwargs) + if isinstance(res, HttpResponseRedirect) and res.url == settings.LOGIN_REDIRECT_URL: + assert hasattr(request, 'user') + # login success + data = UserSerializer(instance=request.user).data + token, created = Token.objects.get_or_create(user=request.user) + data['token'] = token.key + res = Response(data=data) + else: + res = Response({ + 'error': 'An error occurred in social login.', + }, status=status.HTTP_400_BAD_REQUEST) + return api_view.render(res) + + +class TokenOAuth2LoginView(OAuth2LoginView): + def dispatch(self, request, *args, **kwargs): + api_view = SocialLoginView() + api_view.dispatch(request, *args, **kwargs) + res = super(TokenOAuth2LoginView, self).dispatch(request, *args, **kwargs) + if isinstance(res, HttpResponseRedirect) and res.url: + url = res.url + data = { + 'url': url, + } + try: + queries = url.split('?') + queries = queries[1].split('&') + for query in queries: + if query[:6] == 'state=': + data['state'] = query[6:] + except: + pass + if 'state' not in data: + res = Response({ + 'error': 'An error occurred in social login.' + }, status=status.HTTP_400_BAD_REQUEST) + else: + res = Response(data) + else: + res = Response({ + 'error': 'An error occurred in social login.', + }, status=status.HTTP_400_BAD_REQUEST) + return api_view.render(res) + + +class GoogleOAuth2NoRedirectAdapter(GoogleOAuth2Adapter): + provider_id = GoogleProviderNoRedirect.id + + +oauth2_callback = TokenOAuth2CallbackView.adapter_view(GoogleOAuth2NoRedirectAdapter) +oauth2_login = TokenOAuth2LoginView.adapter_view(GoogleOAuth2NoRedirectAdapter) diff --git a/wadium/wadium/adapters.py b/wadium/wadium/adapters.py index 7e4e27f..653b489 100644 --- a/wadium/wadium/adapters.py +++ b/wadium/wadium/adapters.py @@ -1,30 +1,51 @@ from allauth.account.adapter import DefaultAccountAdapter from allauth.socialaccount.adapter import DefaultSocialAccountAdapter -from user.models import UserProfile +from user.models import UserProfile, EmailAddress from rest_framework.authtoken.models import Token +from rest_framework.serializers import ValidationError +from allauth.exceptions import ImmediateHttpResponse +from django.http import HttpResponseBadRequest + + +class WadiumAccountAdapter(DefaultAccountAdapter): + def is_open_for_signup(self, request): + return False # disable account/email signup class WadiumSocialAccountAdapter(DefaultSocialAccountAdapter): - def populate_user(self, request, sociallogin, data): - # print(sociallogin) - # print(data) - # print(sociallogin.account.extra_data) - return super(WadiumSocialAccountAdapter, self).populate_user(request, sociallogin, data) + def is_open_for_signup(self, request, sociallogin): + return True def save_user(self, request, sociallogin, form=None): - print(type(sociallogin.account)) - breakpoint() + email = sociallogin.account.extra_data['email'] + try: + email_address = EmailAddress.objects.get(email=email) + if not email_address.available: + # 해당 이메일로 이미 가입한 유저가 있는지 확인 (이메일 회원가입으로 가입 후 구글 로그인으로 가입하는 경우) + user = email_address.user + sociallogin.user = user + sociallogin.save(request) + return user + except EmailAddress.DoesNotExist: + pass userprofile = { - 'email': sociallogin.account.extra_data['email'], + 'email': email, 'name': sociallogin.account.extra_data['name'], 'profile_image': sociallogin.account.extra_data['picture'] } - user = UserProfile.create_user( - UserProfile.get_unique_username(sociallogin.account.extra_data['email']), - userprofile) + try: + user = UserProfile.create_user( + UserProfile.get_unique_username(sociallogin.account.extra_data['email']), + userprofile) + except ValidationError as e: + res = HttpResponseBadRequest(str(e.detail)) + raise ImmediateHttpResponse(res) + Token.objects.create(user=user) - print(sociallogin.account.provider) - # TODO create usergoogle, userfacebook + # print(sociallogin.account.provider) + # TODO create usergoogle(id, email_address, ...), userfacebook(id) + # TODO usergoogle은 처음에 인증 정보를 저장하기 위해 만든 model인데, allauth를 사용하면서 필요가 없어졌습니다. + # TODO connections 확인 용도 외에는 필요가 없어서 usergoogle이나 userfacebook 구현은 미뤄도 될 것 같습니다. sociallogin.user = user sociallogin.save(request) return user diff --git a/wadium/wadium/settings.py b/wadium/wadium/settings.py index 19998c7..b61f40a 100644 --- a/wadium/wadium/settings.py +++ b/wadium/wadium/settings.py @@ -47,13 +47,14 @@ 'story.apps.StoryConfig', 'django.contrib.sites', - #allauth + # allauth 'allauth', 'allauth.account', 'allauth.socialaccount', - #provider - 'allauth.socialaccount.providers.google', + # provider + 'user.providers.google', + # 'allauth.socialaccount.providers.google', # 'allauth.socialaccount.providers.facebook', ] @@ -178,10 +179,12 @@ SOCIALACCOUNT_QUERY_EMAIL = True SITE_ID = 2 -LOGIN_REDIRECT_URL = '/' ACCOUNT_EMAIL_REQUIRED = True SOCIALACCOUNT_EMAIL_REQUIRED = True SOCIALACCOUNT_AUTO_SIGNUP = True -SOCIALACCOUNT_ADAPTER = "wadium.adapters.WadiumSocialAccountAdapter" \ No newline at end of file +ACCOUNT_ADAPTER = "wadium.adapters.WadiumAccountAdapter" +SOCIALACCOUNT_ADAPTER = "wadium.adapters.WadiumSocialAccountAdapter" + +LOGIN_REDIRECT_URL = '/login/success/' # Not redirected, but used to check From fc76c24ea8cc522a32de1c7f6cefdddeb6e9d3bd Mon Sep 17 00:00:00 2001 From: yuri-0415 <63419338+yuri-0415@users.noreply.github.com> Date: Wed, 6 Jan 2021 00:53:39 +0900 Subject: [PATCH 05/11] update requirements.txt --- wadium/user/serializers.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/wadium/user/serializers.py b/wadium/user/serializers.py index a754f35..6675263 100644 --- a/wadium/user/serializers.py +++ b/wadium/user/serializers.py @@ -192,7 +192,7 @@ class UserSelfSerializer(serializers.ModelSerializer): name = serializers.CharField() bio = serializers.CharField(required=False) profile_image = serializers.URLField(required=False) - email = serializers.CharField() + email = serializers.CharField(read_only=True) connection = serializers.SerializerMethodField() class Meta: model = UserProfile @@ -207,20 +207,11 @@ def get_connection(self, user): return UserSocialSerializer(user, context=self.context).data class UserSocialSerializer(serializers.ModelSerializer): - # google = serializers.SerializerMethodField() - # facebook = serializers.SerializerMethodField() google = serializers.CharField(source='user.usergoogle.google_sub', required=False) - facebook = serializers.CharField(source='user.userfacebook.facebook_sub', required=False) + facebook = serializers.CharField(source='user.userfacebook.facebook_id', required=False) class Meta: model = User fields = ( 'google', 'facebook', ) - -# def get_google(self, user): -# return UserGoogle.objects.filter(user=user.user).last() - -# def get_facebook(self, user): -# return UserFacebook.objects.filter(user=user.user).last() - From 4cb04680808329a5cbcaada3adc84a64e4476d1f Mon Sep 17 00:00:00 2001 From: yuri-0415 <63419338+yuri-0415@users.noreply.github.com> Date: Wed, 6 Jan 2021 03:02:14 +0900 Subject: [PATCH 06/11] update facebook using default provider --- wadium/user/providers/facebook/__init__.py | 0 wadium/user/providers/facebook/provider.py | 9 ++ wadium/user/providers/facebook/urls.py | 9 ++ wadium/user/providers/facebook/views.py | 98 ++++++++++++++++++++++ wadium/user/views.py | 7 +- 5 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 wadium/user/providers/facebook/__init__.py create mode 100644 wadium/user/providers/facebook/provider.py create mode 100644 wadium/user/providers/facebook/urls.py create mode 100644 wadium/user/providers/facebook/views.py diff --git a/wadium/user/providers/facebook/__init__.py b/wadium/user/providers/facebook/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wadium/user/providers/facebook/provider.py b/wadium/user/providers/facebook/provider.py new file mode 100644 index 0000000..338ff3d --- /dev/null +++ b/wadium/user/providers/facebook/provider.py @@ -0,0 +1,9 @@ +from allauth.socialaccount.providers.facebook.provider import FacebookProvider + + +class FacebookProviderNoRedirect(FacebookProvider): + id = 'facebook_no_redirect' + name = 'Custom Facebook' + + +provider_classes = [FacebookProviderNoRedirect] \ No newline at end of file diff --git a/wadium/user/providers/facebook/urls.py b/wadium/user/providers/facebook/urls.py new file mode 100644 index 0000000..c160ad8 --- /dev/null +++ b/wadium/user/providers/facebook/urls.py @@ -0,0 +1,9 @@ +from django.urls import path +from .views import oauth2_callback, oauth2_login +from .provider import FacebookProviderNoRedirect +from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns + +urlpatterns = [ + path("facebook/login/", oauth2_login, name='facebook_no_redirect_login'), + path("facebook/login/callback/", oauth2_callback, name='facebook_no_redirect_callback') +] \ No newline at end of file diff --git a/wadium/user/providers/facebook/views.py b/wadium/user/providers/facebook/views.py new file mode 100644 index 0000000..84ad6bf --- /dev/null +++ b/wadium/user/providers/facebook/views.py @@ -0,0 +1,98 @@ +from allauth.socialaccount.providers.facebook.views import ( + OAuth2CallbackView, + OAuth2LoginView, + FacebookOAuth2Adapter, +) +from django.http import HttpResponseRedirect +from django.conf import settings +from .provider import FacebookProviderNoRedirect +from user.serializers import UserSerializer +from rest_framework.authtoken.models import Token +from rest_framework.response import Response +from rest_framework.views import APIView +from rest_framework import status + + +class SocialLoginView(APIView): + def dispatch(self, request, *args, **kwargs): + self.args = args + self.kwargs = kwargs + request = self.initialize_request(request, *args, **kwargs) + self.request = request + self.headers = self.default_response_headers # deprecate? + + try: + self.initial(request, *args, **kwargs) + except Exception as exc: + pass + + def render(self, response): + query_string = self.request.META.get('QUERY_STRING') + if query_string: + queries = query_string.split('&') + for i in range(len(queries)): + if 'state' in queries[i]: + queries[i] = 'state=' + 'x' * 12 + elif 'code' in queries[i]: + queries[i] = 'code=' + 'x' * 75 + self.request.META['QUERY_STRING'] = '&'.join(queries) + self.response = self.finalize_response(self.request, response, *self.args, **self.kwargs) + return self.response + + +class TokenOAuth2CallbackView(OAuth2CallbackView): + def dispatch(self, request, *args, **kwargs): + api_view = SocialLoginView() + api_view.dispatch(request, *args, **kwargs) + res = super(TokenOAuth2CallbackView, self).dispatch(request, *args, **kwargs) + if isinstance(res, HttpResponseRedirect) and res.url == settings.LOGIN_REDIRECT_URL: + assert hasattr(request, 'user') + # login success + data = UserSerializer(instance=request.user).data + token, created = Token.objects.get_or_create(user=request.user) + data['token'] = token.key + res = Response(data=data) + else: + res = Response({ + 'error': 'An error occurred in social login.', + }, status=status.HTTP_400_BAD_REQUEST) + return api_view.render(res) + + +class TokenOAuth2LoginView(OAuth2LoginView): + def dispatch(self, request, *args, **kwargs): + api_view = SocialLoginView() + api_view.dispatch(request, *args, **kwargs) + res = super(TokenOAuth2LoginView, self).dispatch(request, *args, **kwargs) + if isinstance(res, HttpResponseRedirect) and res.url: + url = res.url + data = { + 'url': url, + } + try: + queries = url.split('?') + queries = queries[1].split('&') + for query in queries: + if query[:6] == 'state=': + data['state'] = query[6:] + except: + pass + if 'state' not in data: + res = Response({ + 'error': 'An error occurred in social login.' + }, status=status.HTTP_400_BAD_REQUEST) + else: + res = Response(data) + else: + res = Response({ + 'error': 'An error occurred in social login.', + }, status=status.HTTP_400_BAD_REQUEST) + return api_view.render(res) + + +class FacebookOAuth2NoRedirectAdapter(FacebookOAuth2Adapter): + provider_id = FacebookProviderNoRedirect.id + + +oauth2_callback = TokenOAuth2CallbackView.adapter_view(FacebookOAuth2NoRedirectAdapter) +oauth2_login = TokenOAuth2LoginView.adapter_view(FacebookOAuth2NoRedirectAdapter) \ No newline at end of file diff --git a/wadium/user/views.py b/wadium/user/views.py index bacf2d3..7a6945d 100644 --- a/wadium/user/views.py +++ b/wadium/user/views.py @@ -146,9 +146,4 @@ def get_serializer_class(self): if self.action == 'update' or self.action == 'retrieve': return UserSelfSerializer else: - return UserSerializer - -#class UserSocialLoginViewSet(viwesets.GenericViewSet): -# queryset = User.objects.all() -# serializer_class = SocialSerializer - + return UserSerializer \ No newline at end of file From d127ff60cf6d5655717ad316bb5774fbd58863f4 Mon Sep 17 00:00:00 2001 From: yuri-0415 <63419338+yuri-0415@users.noreply.github.com> Date: Wed, 6 Jan 2021 03:13:00 +0900 Subject: [PATCH 07/11] update --- wadium/requirements.txt | Bin 1768 -> 1040 bytes wadium/wadium/adapters.py | 25 +++++++++++++++++-------- wadium/wadium/settings.py | 37 +++++++++++++++++++++++-------------- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/wadium/requirements.txt b/wadium/requirements.txt index 7743816c1ed219bd204bf5fabf139ce4aa250d5d..bc77d94d6612a931360f0ea094a4f0b4b9e4c4cc 100644 GIT binary patch delta 93 zcmaFCJAq?@(#En)jFY!8_f7u55;u7Zi^Jpq*1XA=SY;;Hv8hZx#U?j-6Qht(3PUDC iF@r4-nlR`w7%~`vNnQpnh9ZVk2866JP}~%VjTr!PyB7ff literal 1768 zcmZ`)%W|7Q5S(+W@+r6i$gbp&n@>6A;3}U5=;a830VhsAK1ug9yNe{dRFq`4ccve+ z>wkaOHrmb(HrU3t_7$JfZv4sZqkXUn@7MNX6EItRAA#@i-P_91E11-drnW1u7fy-Po(OU`-Tj-NkzM_JkTI)aqglaw?rovslf5JK`hP_OaH= zT@br-JQ-AAu(K@sR$>2e)=7Ds?__Et_GC~%jV=EE1%`wDh<(ZOx@*|^_CC&lk|IVmt*UMF^?~z%CIUo<=$)RxlQt|eV9{Eg2se{kle?*-Peox+06Tm6@e8q`f zIiqqrb(8MPZbD{2Px$oSrvkr_I+bb4&Uca#XTOQaa*^p0D-Xumg%-I;Qx8Wv^z4ut zyOHUwPrR7E$0&zpCc-mzK>ri;azEY3=X6`VJAmO6JmW-!ghL-@Dpf6f9_<;}OOJI% zL#Hmf$|Nf9sL2QumGp3a)BTYzd||4*Wlmrk9OgMzip$l^eQHpNb0&wFMarD$8j102 z@Vx4TaGkdY-T4m0H+!^)=uetc?oRX7coLkipOIING?KQ2F5zc~zC_c1>Yx+yi+xr5 z&)hZKX7Za!JVTYvs&t7nyD8O64Eu$dPVRGF*jHNVf#!gI-nsq}{9c`f&P&q6d%MOe zZ>Mx#bY6SBS>7zv$C}Bz_jWH&IhpC9BjNu98ExRPZUX9-VohMmayc79ydd8L65C)d acuLp)J9tlS3CA0kGg?ONh2P$~zy1Ry?g-)l diff --git a/wadium/wadium/adapters.py b/wadium/wadium/adapters.py index 653b489..0a68f8e 100644 --- a/wadium/wadium/adapters.py +++ b/wadium/wadium/adapters.py @@ -5,7 +5,7 @@ from rest_framework.serializers import ValidationError from allauth.exceptions import ImmediateHttpResponse from django.http import HttpResponseBadRequest - +from user.serializers import * class WadiumAccountAdapter(DefaultAccountAdapter): def is_open_for_signup(self, request): @@ -28,15 +28,24 @@ def save_user(self, request, sociallogin, form=None): return user except EmailAddress.DoesNotExist: pass - userprofile = { - 'email': email, - 'name': sociallogin.account.extra_data['name'], - 'profile_image': sociallogin.account.extra_data['picture'] - } + + if sociallogin.account.provider == 'google': + userprofile = { + 'email': email, + 'name': sociallogin.account.extra_data['name'], + 'profile_image': sociallogin.account.extra_data['picture'] + } + + elif sociallogin.account.provider == "facebook": + userprofile = { + 'email': email, + 'name': sociallogin.account.extra_data['name'], + 'profile_image': sociallogin.account.extra_data['picture']['data']['url'] + } + try: user = UserProfile.create_user( - UserProfile.get_unique_username(sociallogin.account.extra_data['email']), - userprofile) + UserProfile.get_unique_username(sociallogin.account.extra_data['email']), userprofile) except ValidationError as e: res = HttpResponseBadRequest(str(e.detail)) raise ImmediateHttpResponse(res) diff --git a/wadium/wadium/settings.py b/wadium/wadium/settings.py index b61f40a..1253014 100644 --- a/wadium/wadium/settings.py +++ b/wadium/wadium/settings.py @@ -46,6 +46,7 @@ 'user.apps.UserConfig', 'story.apps.StoryConfig', 'django.contrib.sites', + # 'sslserver', # apply https on localhost (temp) # allauth 'allauth', @@ -55,7 +56,8 @@ # provider 'user.providers.google', # 'allauth.socialaccount.providers.google', - # 'allauth.socialaccount.providers.facebook', + 'allauth.socialaccount.providers.facebook', + # 'user.providers.facebook', ] @@ -154,6 +156,26 @@ MAILJET_API_KEY = os.getenv('MAILJET_API_KEY') MAILJET_API_SECRET = os.getenv('MAILJET_API_SECRET') +SOCIALACCOUNT_PROVIDERS = { + 'facebook': { + 'METHOD': 'oauth2', + 'SCOPE': ['email', 'public_profile'], + 'AUTH_PARAMS': {'auth_type': 'reauthenticate'}, + 'FIELDS': [ + 'id', + 'email', + 'name', + 'verified', + 'picture', + 'locale', + 'updated_time'], + 'EXCHANGE_TOKEN': True, + 'LOCALE_FUNC': lambda request: 'kr_KR', + 'VERIFIED_EMAIL': False, + 'VERSION': 'v2.8' + } +} + AUTHENTICATION_BACKENDS = ( # Needed to login by username in Django admin, regardless of `allauth` 'django.contrib.auth.backends.ModelBackend', @@ -162,19 +184,6 @@ 'allauth.account.auth_backends.AuthenticationBackend', ) -SOCIALACCOUNT_PROVIDERS = { - 'google': { - # For each OAuth based provider, either add a ``SocialApp`` - # (``socialaccount`` app) containing the required client - # credentials, or list them here: - 'APP': { - 'client_id': '547861684988-3stvt30tjr8uu0j4llqeh8qpblf0a8uv.apps.googleusercontent.com', - 'secret': 'd-dpDCtOtOlBcrOozo81TJwB', - 'key': '' - } - } -} - ACCOUNT_AUTHENTICATED_LOGIN_REDIRECTS = False SOCIALACCOUNT_QUERY_EMAIL = True From b95e5a3fab1157b2704e056df35363d6bb938750 Mon Sep 17 00:00:00 2001 From: yuri-0415 <63419338+yuri-0415@users.noreply.github.com> Date: Wed, 6 Jan 2021 03:22:09 +0900 Subject: [PATCH 08/11] Update requirements.txt --- wadium/requirements.txt | Bin 1040 -> 1044 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/wadium/requirements.txt b/wadium/requirements.txt index bc77d94d6612a931360f0ea094a4f0b4b9e4c4cc..9472b13d5e97e217be55091dee9d7130316873cc 100644 GIT binary patch delta 12 TcmbQhF@ Date: Wed, 6 Jan 2021 03:27:28 +0900 Subject: [PATCH 09/11] Update requirements.txt --- requirements.txt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/requirements.txt b/requirements.txt index e69de29..4e36122 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,27 @@ +asgiref==3.3.1 +certifi==2020.12.5 +cffi==1.14.4 +chardet==4.0.0 +cryptography==3.3.1 +defusedxml==0.6.0 +Django==3.1.3 +django-allauth==0.44.0 +django-debug-toolbar==3.1.1 +django-rest-framework==0.1.0 +djangorestframework==3.12.2 +idna==2.10 +mailjet-rest==1.3.4 +mysqlclient==2.0.1 +oauthlib==3.1.0 +python-dotenv==0.15.0 +python3-openid==3.2.0 +pycparser==2.20 +PyJWT==2.0.0 +pytz==2020.4 +requests==2.25.1 +requests-oauthlib==1.3.0 +six==1.15.0 +sqlparse==0.4.1 +urllib3==1.26.2 +django-redis==4.12.1 +redis==3.5.3 From 2104e45833d994d82e90aa53a73104b9bd1473d3 Mon Sep 17 00:00:00 2001 From: yuri-0415 <63419338+yuri-0415@users.noreply.github.com> Date: Wed, 6 Jan 2021 03:38:03 +0900 Subject: [PATCH 10/11] update --- wadium/requirements.txt | Bin 1044 -> 1040 bytes wadium/user/views.py | 2 -- 2 files changed, 2 deletions(-) diff --git a/wadium/requirements.txt b/wadium/requirements.txt index 9472b13d5e97e217be55091dee9d7130316873cc..bc77d94d6612a931360f0ea094a4f0b4b9e4c4cc 100644 GIT binary patch delta 7 OcmbQjF@a-)01E&KX94*D delta 12 TcmbQhF@ Date: Wed, 6 Jan 2021 21:42:42 +0900 Subject: [PATCH 11/11] Update requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4154703..923588a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,4 +25,4 @@ sqlparse==0.4.1 urllib3==1.26.2 django-redis==4.12.1 redis==3.5.3 - +django-cors-headers==3.6.0