mirror of
https://github.com/Tivix/django-rest-auth.git
synced 2025-07-22 13:39:45 +03:00
Merge 1371eb932e
into bd97eee65a
This commit is contained in:
commit
391dada483
|
@ -73,3 +73,4 @@ Basing on example from installation section :doc:`Installation </installation>`
|
||||||
- /rest-auth/facebook/ (POST)
|
- /rest-auth/facebook/ (POST)
|
||||||
|
|
||||||
- access_token
|
- access_token
|
||||||
|
- code
|
||||||
|
|
|
@ -6,10 +6,11 @@ from allauth.socialaccount.helpers import complete_social_login
|
||||||
|
|
||||||
class SocialLoginSerializer(serializers.Serializer):
|
class SocialLoginSerializer(serializers.Serializer):
|
||||||
|
|
||||||
access_token = serializers.CharField(required=True)
|
access_token = serializers.CharField(required=False)
|
||||||
|
code = serializers.CharField(required=False)
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
access_token = attrs.get('access_token')
|
|
||||||
view = self.context.get('view')
|
view = self.context.get('view')
|
||||||
request = self.context.get('request')
|
request = self.context.get('request')
|
||||||
if not isinstance(request, HttpRequest):
|
if not isinstance(request, HttpRequest):
|
||||||
|
@ -19,20 +20,71 @@ class SocialLoginSerializer(serializers.Serializer):
|
||||||
raise serializers.ValidationError(
|
raise serializers.ValidationError(
|
||||||
'View is not defined, pass it as a context variable'
|
'View is not defined, pass it as a context variable'
|
||||||
)
|
)
|
||||||
|
|
||||||
self.adapter_class = getattr(view, 'adapter_class', None)
|
self.adapter_class = getattr(view, 'adapter_class', None)
|
||||||
|
|
||||||
if not self.adapter_class:
|
if not self.adapter_class:
|
||||||
raise serializers.ValidationError('Define adapter_class in view')
|
raise serializers.ValidationError(
|
||||||
|
'Define adapter_class in view'
|
||||||
|
)
|
||||||
|
|
||||||
self.adapter = self.adapter_class()
|
self.adapter = self.adapter_class()
|
||||||
app = self.adapter.get_provider().get_app(request)
|
app = self.adapter.get_provider().get_app(request)
|
||||||
|
|
||||||
|
# More info on code vs access_token
|
||||||
|
# http://stackoverflow.com/questions/8666316/facebook-oauth-2-0-code-and-token
|
||||||
|
# We have the access_token straight
|
||||||
|
if('access_token' in attrs):
|
||||||
|
access_token = attrs.get('access_token')
|
||||||
|
# We did not get the access_token, but authorization code instead
|
||||||
|
elif('code' in attrs):
|
||||||
|
self.callback_url = getattr(view, 'callback_url', None)
|
||||||
|
self.client_class = getattr(view, 'client_class', None)
|
||||||
|
|
||||||
|
if not self.callback_url:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
'Define callback_url in view'
|
||||||
|
)
|
||||||
|
if not self.client_class:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
'Define client_class in view'
|
||||||
|
)
|
||||||
|
|
||||||
|
if not self.callback_url:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
'Define callback_url in view'
|
||||||
|
)
|
||||||
|
|
||||||
|
if not self.client_class:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
'Define client_class in view'
|
||||||
|
)
|
||||||
|
|
||||||
|
code = attrs.get('code')
|
||||||
|
|
||||||
|
provider = self.adapter.get_provider()
|
||||||
|
scope = provider.get_scope(request)
|
||||||
|
client = self.client_class(
|
||||||
|
request,
|
||||||
|
app.client_id,
|
||||||
|
app.secret,
|
||||||
|
self.adapter.access_token_method,
|
||||||
|
self.adapter.access_token_url,
|
||||||
|
self.callback_url,
|
||||||
|
scope
|
||||||
|
)
|
||||||
|
token = client.get_access_token(code)
|
||||||
|
access_token = token['access_token']
|
||||||
|
|
||||||
token = self.adapter.parse_token({'access_token': access_token})
|
token = self.adapter.parse_token({'access_token': access_token})
|
||||||
token.app = app
|
token.app = app
|
||||||
|
|
||||||
try:
|
try:
|
||||||
login = self.adapter.complete_login(request, app, token,
|
login = self.adapter.complete_login(
|
||||||
response=access_token)
|
request,
|
||||||
|
app,
|
||||||
|
token,
|
||||||
|
response=access_token,
|
||||||
|
)
|
||||||
|
|
||||||
login.token = token
|
login.token = token
|
||||||
complete_social_login(request, login)
|
complete_social_login(request, login)
|
||||||
|
|
|
@ -2,20 +2,41 @@ from django.http import HttpRequest
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.permissions import AllowAny
|
from rest_framework.permissions import AllowAny
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
from allauth.account.views import SignupView, ConfirmEmailView
|
from allauth.account.views import SignupView, ConfirmEmailView
|
||||||
from allauth.account.utils import complete_signup
|
from allauth.account.utils import complete_signup
|
||||||
from allauth.account import app_settings
|
from allauth.account import app_settings
|
||||||
|
|
||||||
from rest_auth.app_settings import UserDetailsSerializer
|
from rest_auth.app_settings import (
|
||||||
|
UserDetailsSerializer,
|
||||||
|
TokenSerializer,
|
||||||
|
)
|
||||||
from rest_auth.registration.serializers import SocialLoginSerializer
|
from rest_auth.registration.serializers import SocialLoginSerializer
|
||||||
from rest_auth.views import Login
|
from rest_auth.views import (
|
||||||
|
Login,
|
||||||
|
EverybodyCanAuthentication,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Register(APIView, SignupView):
|
class Register(APIView, SignupView):
|
||||||
|
"""
|
||||||
|
Accepts the credentials and creates a new user
|
||||||
|
if user does not exist already
|
||||||
|
Return the REST Token and the user object
|
||||||
|
if the credentials are valid and authenticated.
|
||||||
|
Calls allauth complete_signup method
|
||||||
|
|
||||||
|
Accept the following POST parameters: username, password
|
||||||
|
Return the REST Framework Token Object's key
|
||||||
|
and user object.
|
||||||
|
"""
|
||||||
|
|
||||||
permission_classes = (AllowAny,)
|
permission_classes = (AllowAny,)
|
||||||
|
authentication_classes = (EverybodyCanAuthentication,)
|
||||||
|
token_model = Token
|
||||||
|
token_serializer = TokenSerializer
|
||||||
user_serializer_class = UserDetailsSerializer
|
user_serializer_class = UserDetailsSerializer
|
||||||
allowed_methods = ('POST', 'OPTIONS', 'HEAD')
|
allowed_methods = ('POST', 'OPTIONS', 'HEAD')
|
||||||
|
|
||||||
|
@ -27,6 +48,8 @@ class Register(APIView, SignupView):
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
self.user = form.save(self.request)
|
self.user = form.save(self.request)
|
||||||
|
self.token, created = self.token_model.objects.get_or_create(
|
||||||
|
user=self.user)
|
||||||
if isinstance(self.request, HttpRequest):
|
if isinstance(self.request, HttpRequest):
|
||||||
request = self.request
|
request = self.request
|
||||||
else:
|
else:
|
||||||
|
@ -47,8 +70,10 @@ class Register(APIView, SignupView):
|
||||||
return self.get_response_with_errors()
|
return self.get_response_with_errors()
|
||||||
|
|
||||||
def get_response(self):
|
def get_response(self):
|
||||||
serializer = self.user_serializer_class(instance=self.user)
|
response = self.token_serializer(self.token).data
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
user = self.user_serializer_class(instance=self.user).data
|
||||||
|
response['user'] = user
|
||||||
|
return Response(response, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
def get_response_with_errors(self):
|
def get_response_with_errors(self):
|
||||||
return Response(self.form.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(self.form.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
@ -72,11 +97,25 @@ class VerifyEmail(APIView, ConfirmEmailView):
|
||||||
class SocialLogin(Login):
|
class SocialLogin(Login):
|
||||||
"""
|
"""
|
||||||
class used for social authentications
|
class used for social authentications
|
||||||
example usage for facebook
|
example usage for facebook with access_token
|
||||||
|
|
||||||
|
-------------
|
||||||
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
|
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
|
||||||
|
|
||||||
class FacebookLogin(SocialLogin):
|
class FacebookLogin(SocialLogin):
|
||||||
adapter_class = FacebookOAuth2Adapter
|
adapter_class = FacebookOAuth2Adapter
|
||||||
|
|
||||||
|
-------------
|
||||||
|
example usage for facebook with code
|
||||||
|
|
||||||
|
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
|
||||||
|
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
|
||||||
|
|
||||||
|
class FacebookLogin(SocialLogin):
|
||||||
|
adapter_class = FacebookOAuth2Adapter
|
||||||
|
client_class = OAuth2Client
|
||||||
|
callback_url = 'localhost:8000'
|
||||||
|
-------------
|
||||||
"""
|
"""
|
||||||
|
|
||||||
serializer_class = SocialLoginSerializer
|
serializer_class = SocialLoginSerializer
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth import authenticate
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
|
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
|
||||||
try:
|
try:
|
||||||
from django.utils.http import urlsafe_base64_decode as uid_decoder
|
from django.utils.http import urlsafe_base64_decode as uid_decoder
|
||||||
|
@ -8,16 +10,76 @@ except:
|
||||||
from django.utils.http import base36_to_int as uid_decoder
|
from django.utils.http import base36_to_int as uid_decoder
|
||||||
from django.contrib.auth.tokens import default_token_generator
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import (
|
||||||
|
exceptions,
|
||||||
|
serializers,
|
||||||
|
)
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
from rest_framework.authtoken.serializers import AuthTokenSerializer
|
# from rest_framework.authtoken.serializers import AuthTokenSerializer
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
class LoginSerializer(AuthTokenSerializer):
|
class LoginSerializer(serializers.Serializer):
|
||||||
|
username = serializers.CharField(required=False)
|
||||||
|
email = serializers.EmailField(required=False)
|
||||||
|
password = serializers.CharField(style={'input_type': 'password'})
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
attrs = super(LoginSerializer, self).validate(attrs)
|
username = attrs.get('username')
|
||||||
|
email = attrs.get('email')
|
||||||
|
password = attrs.get('password')
|
||||||
|
|
||||||
|
if 'allauth' in settings.INSTALLED_APPS:
|
||||||
|
from allauth.account import app_settings
|
||||||
|
|
||||||
|
# Authentication through email
|
||||||
|
if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.EMAIL:
|
||||||
|
if email and password:
|
||||||
|
user = authenticate(email=email, password=password)
|
||||||
|
else:
|
||||||
|
msg = _('Must include "email" and "password".')
|
||||||
|
raise exceptions.ValidationError(msg)
|
||||||
|
# Authentication through username
|
||||||
|
elif app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.USERNAME:
|
||||||
|
if username and password:
|
||||||
|
user = authenticate(username=username, password=password)
|
||||||
|
else:
|
||||||
|
msg = _('Must include "username" and "password".')
|
||||||
|
raise exceptions.ValidationError(msg)
|
||||||
|
# Authentication through either username or email
|
||||||
|
else:
|
||||||
|
if email and password:
|
||||||
|
user = authenticate(email=email, password=password)
|
||||||
|
elif username and password:
|
||||||
|
user = authenticate(username=username, password=password)
|
||||||
|
else:
|
||||||
|
msg = _('Must include either "username" or "email" and "password".')
|
||||||
|
raise exceptions.ValidationError(msg)
|
||||||
|
|
||||||
|
if user:
|
||||||
|
if not user.is_active:
|
||||||
|
msg = _('User account is disabled.')
|
||||||
|
raise exceptions.ValidationError(msg)
|
||||||
|
else:
|
||||||
|
msg = _('Unable to log in with provided credentials.')
|
||||||
|
raise exceptions.ValidationError(msg)
|
||||||
|
|
||||||
|
elif username and password:
|
||||||
|
user = authenticate(username=username, password=password)
|
||||||
|
|
||||||
|
if user:
|
||||||
|
if not user.is_active:
|
||||||
|
msg = _('User account is disabled.')
|
||||||
|
raise exceptions.ValidationError(msg)
|
||||||
|
else:
|
||||||
|
msg = _('Unable to log in with provided credentials.')
|
||||||
|
raise exceptions.ValidationError(msg)
|
||||||
|
else:
|
||||||
|
msg = _('Must include "username" and "password".')
|
||||||
|
raise exceptions.ValidationError(msg)
|
||||||
|
|
||||||
|
attrs['user'] = user
|
||||||
|
|
||||||
if 'rest_auth.registration' in settings.INSTALLED_APPS:
|
if 'rest_auth.registration' in settings.INSTALLED_APPS:
|
||||||
from allauth.account import app_settings
|
from allauth.account import app_settings
|
||||||
if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY:
|
if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY:
|
||||||
|
@ -25,9 +87,24 @@ class LoginSerializer(AuthTokenSerializer):
|
||||||
email_address = user.emailaddress_set.get(email=user.email)
|
email_address = user.emailaddress_set.get(email=user.email)
|
||||||
if not email_address.verified:
|
if not email_address.verified:
|
||||||
raise serializers.ValidationError('E-mail is not verified.')
|
raise serializers.ValidationError('E-mail is not verified.')
|
||||||
|
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
# class LoginSerializer(AuthTokenSerializer):
|
||||||
|
|
||||||
|
# def validate(self, attrs):
|
||||||
|
# attrs = super(LoginSerializer, self).validate(attrs)
|
||||||
|
# if 'rest_auth.registration' in settings.INSTALLED_APPS:
|
||||||
|
# from allauth.account import app_settings
|
||||||
|
# if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY:
|
||||||
|
# user = attrs['user']
|
||||||
|
# email_address = user.emailaddress_set.get(email=user.email)
|
||||||
|
# if not email_address.verified:
|
||||||
|
# raise serializers.ValidationError('E-mail is not verified.')
|
||||||
|
# return attrs
|
||||||
|
|
||||||
|
|
||||||
class TokenSerializer(serializers.ModelSerializer):
|
class TokenSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for Token model.
|
Serializer for Token model.
|
||||||
|
|
|
@ -8,29 +8,44 @@ from rest_framework.generics import GenericAPIView
|
||||||
from rest_framework.permissions import IsAuthenticated, AllowAny
|
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
from rest_framework.generics import RetrieveUpdateAPIView
|
from rest_framework.generics import RetrieveUpdateAPIView
|
||||||
|
from rest_framework.authentication import SessionAuthentication
|
||||||
|
|
||||||
from .app_settings import (
|
from .app_settings import (
|
||||||
TokenSerializer, UserDetailsSerializer, LoginSerializer,
|
TokenSerializer,
|
||||||
PasswordResetSerializer, PasswordResetConfirmSerializer,
|
UserDetailsSerializer,
|
||||||
PasswordChangeSerializer
|
LoginSerializer,
|
||||||
|
PasswordResetSerializer,
|
||||||
|
PasswordResetConfirmSerializer,
|
||||||
|
PasswordChangeSerializer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# http://bytefilia.com/titanium-mobile-facebook-application-django-allauth-sign-sign/
|
||||||
|
class EverybodyCanAuthentication(SessionAuthentication):
|
||||||
|
def authenticate(self, request):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class Login(GenericAPIView):
|
class Login(GenericAPIView):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Check the credentials and return the REST Token
|
Check the credentials and return the REST Token
|
||||||
|
and the user object
|
||||||
if the credentials are valid and authenticated.
|
if the credentials are valid and authenticated.
|
||||||
Calls Django Auth login method to register User ID
|
Calls Django Auth login method to register User ID
|
||||||
in Django session framework
|
in Django session framework
|
||||||
|
|
||||||
Accept the following POST parameters: username, password
|
Accept the following POST parameters: username, password
|
||||||
Return the REST Framework Token Object's key.
|
Return the REST Framework Token Object's key
|
||||||
|
and user object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
permission_classes = (AllowAny,)
|
permission_classes = (AllowAny,)
|
||||||
|
authentication_classes = (EverybodyCanAuthentication,)
|
||||||
serializer_class = LoginSerializer
|
serializer_class = LoginSerializer
|
||||||
token_model = Token
|
token_model = Token
|
||||||
response_serializer = TokenSerializer
|
response_serializer = TokenSerializer
|
||||||
|
user_serializer = UserDetailsSerializer
|
||||||
|
|
||||||
def login(self):
|
def login(self):
|
||||||
self.user = self.serializer.validated_data['user']
|
self.user = self.serializer.validated_data['user']
|
||||||
|
@ -40,13 +55,18 @@ class Login(GenericAPIView):
|
||||||
login(self.request, self.user)
|
login(self.request, self.user)
|
||||||
|
|
||||||
def get_response(self):
|
def get_response(self):
|
||||||
|
response = self.response_serializer(self.token).data
|
||||||
|
user = self.user_serializer(instance=self.user).data
|
||||||
|
response['user'] = user
|
||||||
return Response(
|
return Response(
|
||||||
self.response_serializer(self.token).data, status=status.HTTP_200_OK
|
response,
|
||||||
|
status=status.HTTP_200_OK
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_error_response(self):
|
def get_error_response(self):
|
||||||
return Response(
|
return Response(
|
||||||
self.serializer.errors, status=status.HTTP_400_BAD_REQUEST
|
self.serializer.errors,
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
)
|
)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
@ -75,8 +95,10 @@ class Logout(APIView):
|
||||||
|
|
||||||
logout(request)
|
logout(request)
|
||||||
|
|
||||||
return Response({"success": "Successfully logged out."},
|
return Response(
|
||||||
status=status.HTTP_200_OK)
|
{"success": "Successfully logged out."},
|
||||||
|
status=status.HTTP_200_OK
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserDetails(RetrieveUpdateAPIView):
|
class UserDetails(RetrieveUpdateAPIView):
|
||||||
|
@ -127,7 +149,8 @@ class PasswordReset(GenericAPIView):
|
||||||
class PasswordResetConfirm(GenericAPIView):
|
class PasswordResetConfirm(GenericAPIView):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Password reset e-mail link is confirmed, therefore this resets the user's password.
|
Password reset e-mail link is confirmed,
|
||||||
|
therefore this resets the user's password.
|
||||||
|
|
||||||
Accepts the following POST parameters: new_password1, new_password2
|
Accepts the following POST parameters: new_password1, new_password2
|
||||||
Accepts the following Django URL arguments: token, uid
|
Accepts the following Django URL arguments: token, uid
|
||||||
|
@ -141,10 +164,13 @@ class PasswordResetConfirm(GenericAPIView):
|
||||||
serializer = self.get_serializer(data=request.DATA)
|
serializer = self.get_serializer(data=request.DATA)
|
||||||
if not serializer.is_valid():
|
if not serializer.is_valid():
|
||||||
return Response(
|
return Response(
|
||||||
serializer.errors, status=status.HTTP_400_BAD_REQUEST
|
serializer.errors,
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
)
|
)
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return Response({"success": "Password has been reset with the new password."})
|
return Response(
|
||||||
|
{"success": "Password has been reset with the new password."}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PasswordChange(GenericAPIView):
|
class PasswordChange(GenericAPIView):
|
||||||
|
@ -163,7 +189,10 @@ class PasswordChange(GenericAPIView):
|
||||||
serializer = self.get_serializer(data=request.DATA)
|
serializer = self.get_serializer(data=request.DATA)
|
||||||
if not serializer.is_valid():
|
if not serializer.is_valid():
|
||||||
return Response(
|
return Response(
|
||||||
serializer.errors, status=status.HTTP_400_BAD_REQUEST
|
serializer.errors,
|
||||||
|
status=status.HTTP_400_BAD_REQUEST
|
||||||
)
|
)
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return Response({"success": "New password has been saved."})
|
return Response(
|
||||||
|
{"success": "New password has been saved."}
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user