import json import jwt from jwt.algorithms import RSAAlgorithm import requests from django.contrib.auth.models import User from django.shortcuts import render from django.urls import reverse from rest_framework import generics, status, serializers, HTTP_HEADER_ENCODING, permissions from rest_framework.decorators import api_view, authentication_classes, permission_classes from rest_framework.response import Response from knox.auth import TokenAuthentication from knox.models import AuthToken from core.models.social_account import SocialAccount from core.serializers.login import LoginSerializer from core.serializers.register import RegisterSerializer from core.serializers.user import UserSerializer from core.serializers.socialAuthSerializer import AppleUserInputSerializer, FacebookUserInputSerializer, GoogleUserInputSerializer class RegisterAPI(generics.GenericAPIView): serializer_class = RegisterSerializer def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) user = serializer.save() user.is_active = False user.save() token = AuthToken.objects.create(user) return Response({ "user": UserSerializer(user, context=self.get_serializer_context()).data, "token": token[1] }) class LoginAPI(generics.GenericAPIView): serializer_class = LoginSerializer def post(self, request): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) user = serializer.validated_data token = AuthToken.objects.create(user) return Response({ "user": UserSerializer(user, context=self.get_serializer_context()).data, "token": token[1], }) class AppleLogin(generics.GenericAPIView): serializer_class = AppleUserInputSerializer APPLE_PUBLIC_KEY_URL = "https://appleid.apple.com/auth/keys" APPLE_APP_ID = "com.hamsterwhat.ios" def _decode_apple_user_token(self, apple_user_token): key_payload = requests.get(self.APPLE_PUBLIC_KEY_URL).json() for public_key in key_payload["keys"]: public_key = RSAAlgorithm.from_jwk(json.dumps(public_key)) try: token = jwt.decode(apple_user_token, public_key, audience=[self.APPLE_APP_ID, 'host.exp.Exponent'], algorithms=['RS256']) except jwt.exceptions.ExpiredSignatureError as e: serializers.ValidationError({"id_token": "That token has expired."}) except jwt.exceptions.InvalidAudienceError as e: serializers.ValidationError({"id_token": "That token's audience did not match."}) except Exception as e: continue if token is None: serializers.ValidationError({"id_token": "That token is invalid."}) return token def post(self, request): print(request.data) serializer = self.serializer_class(data=request.data) serializer.is_valid(raise_exception=True) id_token = serializer.validated_data.get('id_token') data_from_id_token = self._decode_apple_user_token(id_token) print(data_from_id_token) identity = 'apple_' + data_from_id_token.get('sub') if SocialAccount.objects.filter(identity=identity).exists(): user = SocialAccount.objects.filter(identity=identity).first().user else: user, created = User.objects.get_or_create( username=identity, password=User.objects.make_random_password(), email=data_from_id_token.get('email', None), ) social_account = SocialAccount(identity=identity, user=user) social_account.save() token = AuthToken.objects.create(user) return Response({ "user": UserSerializer(user, context=self.get_serializer_context()).data, "token": token[1], }) class GoogleLogin(generics.GenericAPIView): serializer_class = GoogleUserInputSerializer GOOGLE_API = "https://www.googleapis.com/userinfo/v2/me" def post(self, request): serializer = self.serializer_class(data=request.data) serializer.is_valid(raise_exception=True) access_token = serializer.validated_data.get('access_token') req = requests.get(self.GOOGLE_API, params={'access_token': access_token}) data_from_api = req.json() identity = 'google_' + data_from_api.get('id') if SocialAccount.objects.filter(identity=identity).exists(): user = SocialAccount.objects.filter(identity=identity).first().user else: user, created = User.objects.get_or_create( username=identity, password=User.objects.make_random_password(), email=data_from_api.get('email', None), ) if created: user.first_name = data_from_api.get('given_name', user.first_name) user.last_name = data_from_api.get('family_name', user.last_name) user.save() user.profile.photo = data_from_api.get('picture', user.profile.photo) user.profile.save() social_account = SocialAccount(identity=identity, user=user) social_account.save() token = AuthToken.objects.create(user) return Response({ "user": UserSerializer(user, context=self.get_serializer_context()).data, "token": token[1], }) class FacebookLogin(generics.GenericAPIView): serializer_class = FacebookUserInputSerializer FACEBOOK_API = "https://graph.facebook.com/me" def post(self, request): serializer = self.serializer_class(data=request.data) serializer.is_valid(raise_exception=True) access_token = serializer.validated_data.get('access_token') req = requests.get(self.FACEBOOK_API, params={'fields': 'id,name,email,first_name,last_name,picture', 'access_token': access_token}) data_from_api = req.json() identity = 'fb_' + data_from_api.get('id') if SocialAccount.objects.filter(identity=identity).exists(): user = SocialAccount.objects.filter(identity=identity).first().user else: user, created = User.objects.get_or_create( username=identity, password=User.objects.make_random_password(), email=data_from_api.get('email', None), ) if created: user.first_name = data_from_api.get('first_name', user.first_name) user.last_name = data_from_api.get('last_name', user.last_name) user.save() user.profile.photo = data_from_api.get('picture', {}).get('data', {}).get('url', user.profile.photo) user.profile.save() social_account = SocialAccount(identity=identity, user=user) social_account.save() token = AuthToken.objects.create(user) return Response({ "user": UserSerializer(user, context=self.get_serializer_context()).data, "token": token[1], }) @api_view(['GET']) @authentication_classes([]) def validate_token(request): try: authenticator = TokenAuthentication() user, auth_token = authenticator.authenticate(request) if user and auth_token: return Response({'valid': 'true'}) except: return Response({'valid': 'false'}) @api_view(['DELETE']) @authentication_classes([]) # @permission_classes([permissions.IsAuthenticated]) def delete_account(request): try: authenticator = TokenAuthentication() user, auth_token = authenticator.authenticate(request) if user and auth_token: user.delete() return Response({'msg': 'Delete successfully.'}) except: return Response({'msg': 'Failed to delete this account.'}, status=status.HTTP_401_UNAUTHORIZED) def verify_user_and_activate(request, token): try: auth = AuthToken.objects.filter(digest=token).first() auth.user.is_active = True auth.user.save() return render( request, template_name='email/verification_success.html', context={ 'msg': 'Your Email is verified successfully and account has been activated.', 'status': 'Verification Successful!', } ) except: return render( request, template_name='email/verification_fail.html', context={ 'msg': 'There is something wrong with this link, unable to verify the user...', 'minor_msg': 'There is something wrong with this link...', 'status': 'Verification Failed!', } )