Skip to content

Also accepting authorization codes instead of access tokens #72

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f9f9d56
Social login also accepts an authentication code instead of an access…
philippeluickx Apr 12, 2015
8e9ddd9
some pep cleaning
philippeluickx Apr 12, 2015
8c74459
dummy auth provider to ensure that not logged in users can have access
philippeluickx Apr 12, 2015
482e6be
Bugfix serializer
philippeluickx Apr 12, 2015
664a4a0
callback url fix
philippeluickx Apr 12, 2015
db0b509
client class
philippeluickx Apr 12, 2015
724f17d
client class fix
philippeluickx Apr 12, 2015
901173a
config error msg
philippeluickx Apr 13, 2015
6450439
user added to response on login
philippeluickx Apr 14, 2015
71a1071
register update
philippeluickx Apr 14, 2015
38607cd
now I get why response_serializer is used...
philippeluickx Apr 14, 2015
cd20ad4
login through email possible, checking allauth settings
philippeluickx Apr 15, 2015
bb5ac3e
Social login also accepts an authentication code instead of an access…
philippeluickx Apr 12, 2015
c7ef9eb
some pep cleaning
philippeluickx Apr 12, 2015
84ac7e9
dummy auth provider to ensure that not logged in users can have access
philippeluickx Apr 12, 2015
b65ce17
Bugfix serializer
philippeluickx Apr 12, 2015
55cc830
callback url fix
philippeluickx Apr 12, 2015
68c0a7f
client class
philippeluickx Apr 12, 2015
dcd88a3
client class fix
philippeluickx Apr 12, 2015
eb616d1
config error msg
philippeluickx Apr 13, 2015
75f67aa
user added to response on login
philippeluickx Apr 14, 2015
2fff7a6
register update
philippeluickx Apr 14, 2015
4b18802
now I get why response_serializer is used...
philippeluickx Apr 14, 2015
a18c450
login through email possible, checking allauth settings
philippeluickx Apr 15, 2015
2c2a60c
Merge branch 'master' of https://github.com/philippeluickx/django-res…
philippeluickx Apr 15, 2015
44ff8a9
merge commit
philippeluickx Apr 18, 2015
4bb8c8d
Merge branch 'master' of https://github.com/philippeluickx/django-res…
philippeluickx Apr 27, 2015
ec25bbf
everybody can auth
philippeluickx Apr 27, 2015
a6a2e12
merge commit with upstream
philippeluickx Apr 30, 2015
d0cd73e
Merge branch 'master' of https://github.com/philippeluickx/django-res…
philippeluickx Apr 30, 2015
1371eb9
cleanup
philippeluickx Apr 30, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/api_endpoints.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,4 @@ Basing on example from installation section :doc:`Installation </installation>`
- /rest-auth/facebook/ (POST)

- access_token
- code
64 changes: 58 additions & 6 deletions rest_auth/registration/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@

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):
access_token = attrs.get('access_token')

view = self.context.get('view')
request = self.context.get('request')
if not isinstance(request, HttpRequest):
Expand All @@ -19,20 +20,71 @@ def validate(self, attrs):
raise serializers.ValidationError(
'View is not defined, pass it as a context variable'
)

self.adapter_class = getattr(view, 'adapter_class', None)

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()
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.app = app

try:
login = self.adapter.complete_login(request, app, token,
response=access_token)
login = self.adapter.complete_login(
request,
app,
token,
response=access_token,
)

login.token = token
complete_social_login(request, login)
Expand Down
49 changes: 44 additions & 5 deletions rest_auth/registration/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,41 @@
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from rest_framework.authtoken.models import Token
from rest_framework import status

from allauth.account.views import SignupView, ConfirmEmailView
from allauth.account.utils import complete_signup
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.views import Login
from rest_auth.views import (
Login,
EverybodyCanAuthentication,
)


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,)
authentication_classes = (EverybodyCanAuthentication,)
token_model = Token
token_serializer = TokenSerializer
user_serializer_class = UserDetailsSerializer
allowed_methods = ('POST', 'OPTIONS', 'HEAD')

Expand All @@ -27,6 +48,8 @@ def put(self, *args, **kwargs):

def form_valid(self, form):
self.user = form.save(self.request)
self.token, created = self.token_model.objects.get_or_create(
user=self.user)
if isinstance(self.request, HttpRequest):
request = self.request
else:
Expand All @@ -47,8 +70,10 @@ def post(self, request, *args, **kwargs):
return self.get_response_with_errors()

def get_response(self):
serializer = self.user_serializer_class(instance=self.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
response = self.token_serializer(self.token).data
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):
return Response(self.form.errors, status=status.HTTP_400_BAD_REQUEST)
Expand All @@ -72,11 +97,25 @@ def post(self, request, *args, **kwargs):
class SocialLogin(Login):
"""
class used for social authentications
example usage for facebook
example usage for facebook with access_token

-------------
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter

class FacebookLogin(SocialLogin):
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
85 changes: 81 additions & 4 deletions rest_auth/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from django.contrib.auth import get_user_model
from django.contrib.auth import authenticate
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
try:
from django.utils.http import urlsafe_base64_decode as uid_decoder
Expand All @@ -8,26 +10,101 @@
from django.utils.http import base36_to_int as uid_decoder
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.serializers import AuthTokenSerializer
# from rest_framework.authtoken.serializers import AuthTokenSerializer
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):
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:
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 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):
"""
Serializer for Token model.
Expand Down
Loading