├── README.md ├── beers ├── __init__.py ├── admin.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_beer_owner.py │ └── __init__.py ├── models.py ├── serializer.py ├── tests.py ├── urls.py └── views.py ├── beerstash ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py ├── manage.py ├── requirements.txt └── users ├── __init__.py ├── admin.py ├── migrations └── __init__.py ├── models.py ├── serializer.py ├── tests.py ├── tools.py ├── urls.py └── views.py /README.md: -------------------------------------------------------------------------------- 1 | # Django-Oauth-Toolkit-Python-Social-Auth-Integration 2 | REST Framework + Django OAuth Toolkit + Python Social Auth integration 3 | 4 | *See [PhilipGarnero/django-rest-framework-social-oauth2](https://github.com/PhilipGarnero/django-rest-framework-social-oauth2) for a more complete solution.* 5 | 6 | A solution for the common problem of making Django OAuth Toolkit working with Python Social Auth. 7 | 8 | The trick is to register/sign in users from third parties access tokens and hooking up in the process to create your own access token and return it as JSON. 9 | 10 | Feel free to create pull requests if you have a better solution! 11 | -------------------------------------------------------------------------------- /beers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felix-d/Django-Oauth-Toolkit-Python-Social-Auth-Integration/6645ea8e7e70b1fd0c61a35c8997288ec52fe5f7/beers/__init__.py -------------------------------------------------------------------------------- /beers/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /beers/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='Beer', 15 | fields=[ 16 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 17 | ('created', models.DateTimeField(auto_now_add=True)), 18 | ('brand', models.CharField(default=b'', max_length=100)), 19 | ('beer_type', models.CharField(default=b'', max_length=100)), 20 | ('ml', models.IntegerField(default=330)), 21 | ], 22 | options={ 23 | 'ordering': ('created',), 24 | }, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /beers/migrations/0002_beer_owner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('beers', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='beer', 18 | name='owner', 19 | field=models.ForeignKey(related_name='beers', default=None, to=settings.AUTH_USER_MODEL), 20 | preserve_default=False, 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /beers/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felix-d/Django-Oauth-Toolkit-Python-Social-Auth-Integration/6645ea8e7e70b1fd0c61a35c8997288ec52fe5f7/beers/migrations/__init__.py -------------------------------------------------------------------------------- /beers/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | # Create your models here. 5 | class Beer(models.Model): 6 | created = models.DateTimeField(auto_now_add=True) 7 | brand = models.CharField(max_length=100, default='') 8 | beer_type = models.CharField(max_length=100, default='') 9 | ml = models.IntegerField(default=330) 10 | owner = models.ForeignKey('auth.User', related_name="beers") 11 | 12 | class Meta: 13 | ordering = ('created',) 14 | -------------------------------------------------------------------------------- /beers/serializer.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from .models import Beer 3 | 4 | 5 | class BeerSerializer(serializers.ModelSerializer): 6 | class Meta: 7 | model = Beer 8 | fields = ('id', 'brand', 'beer_type', 'ml') 9 | -------------------------------------------------------------------------------- /beers/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /beers/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url, patterns 2 | from rest_framework.urlpatterns import format_suffix_patterns 3 | from . import views 4 | 5 | urlpatterns = patterns( 6 | '', 7 | url(r'^$', views.BeerList.as_view()), 8 | url(r'^(?P[0-9]+)/$', views.BeerDetail.as_view()), 9 | ) 10 | 11 | urlpatterns = format_suffix_patterns(urlpatterns) 12 | -------------------------------------------------------------------------------- /beers/views.py: -------------------------------------------------------------------------------- 1 | from beers.models import Beer 2 | from beers.serializer import BeerSerializer 3 | from rest_framework import generics, permissions 4 | 5 | 6 | # List our beers and add beers 7 | class BeerList(generics.ListCreateAPIView): 8 | serializer_class = BeerSerializer 9 | permission_classes = (permissions.IsAuthenticated,) 10 | 11 | def get_queryset(self): 12 | user = self.request.user 13 | return Beer.objects.filter(owner=user) 14 | 15 | def perform_create(self, serializer): 16 | serializer.save(owner=self.request.user) 17 | 18 | 19 | # Get a beer and remove a beer 20 | class BeerDetail(generics.RetrieveDestroyAPIView): 21 | serializer_class = BeerSerializer 22 | permission_classes = (permissions.IsAuthenticated,) 23 | 24 | def get_queryset(self): 25 | user = self.request.user 26 | return Beer.objects.filter(owner=user) 27 | -------------------------------------------------------------------------------- /beerstash/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felix-d/Django-Oauth-Toolkit-Python-Social-Auth-Integration/6645ea8e7e70b1fd0c61a35c8997288ec52fe5f7/beerstash/__init__.py -------------------------------------------------------------------------------- /beerstash/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for beerstash project. 3 | 4 | For more information on this file, see 5 | https://docs.djangoproject.com/en/1.7/topics/settings/ 6 | 7 | For the full list of settings and their values, see 8 | https://docs.djangoproject.com/en/1.7/ref/settings/ 9 | """ 10 | 11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 12 | import os 13 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 14 | 15 | 16 | # Quick-start development settings - unsuitable for production 17 | # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ 18 | 19 | # SECURITY WARNING: keep the secret key used in production secret! 20 | SECRET_KEY = 'hd#x!ysy@y+^*%i+klb)o0by!bh&7nu3uhg+5r0m=$3x$a!j@9' 21 | 22 | # SECURITY WARNING: don't run with debug turned on in production! 23 | DEBUG = True 24 | 25 | TEMPLATE_DEBUG = True 26 | 27 | ALLOWED_HOSTS = [] 28 | 29 | TEMPLATE_CONTEXT_PROCESSORS = ( 30 | 'django.contrib.auth.context_processors.auth', 31 | 'social.apps.django_app.context_processors.backends', 32 | 'social.apps.django_app.context_processors.login_redirect', 33 | ) 34 | 35 | # SOCIAL_AUTH_NEW_USER_REDIRECT_URL = '/new-users-redirect-url/' 36 | # SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/users/login/' 37 | # Application definition 38 | 39 | INSTALLED_APPS = ( 40 | 'django.contrib.admin', 41 | 'django.contrib.auth', 42 | 'django.contrib.contenttypes', 43 | 'django.contrib.sessions', 44 | 'django.contrib.messages', 45 | 'django.contrib.staticfiles', 46 | 'social.apps.django_app.default', 47 | 'oauth2_provider', 48 | 'rest_framework', 49 | 'beers', 50 | ) 51 | 52 | MIDDLEWARE_CLASSES = ( 53 | 'django.contrib.sessions.middleware.SessionMiddleware', 54 | 'django.middleware.common.CommonMiddleware', 55 | 'django.middleware.csrf.CsrfViewMiddleware', 56 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 57 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 58 | 'django.contrib.messages.middleware.MessageMiddleware', 59 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 60 | 'social.apps.django_app.middleware.SocialAuthExceptionMiddleware', 61 | ) 62 | 63 | AUTHENTICATION_BACKENDS = ( 64 | 'social.backends.facebook.FacebookOAuth2', 65 | 'django.contrib.auth.backends.ModelBackend', 66 | ) 67 | ROOT_URLCONF = 'beerstash.urls' 68 | 69 | WSGI_APPLICATION = 'beerstash.wsgi.application' 70 | 71 | 72 | # Database 73 | # https://docs.djangoproject.com/en/1.7/ref/settings/#databases 74 | 75 | DATABASES = { 76 | 'default': { 77 | 'ENGINE': 'django.db.backends.sqlite3', 78 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 79 | } 80 | } 81 | 82 | # Internationalization 83 | # https://docs.djangoproject.com/en/1.7/topics/i18n/ 84 | 85 | LANGUAGE_CODE = 'en-us' 86 | 87 | TIME_ZONE = 'UTC' 88 | 89 | USE_I18N = True 90 | 91 | USE_L10N = True 92 | 93 | USE_TZ = True 94 | 95 | 96 | # Static files (CSS, JavaScript, Images) 97 | # https://docs.djangoproject.com/en/1.7/howto/static-files/ 98 | 99 | STATIC_URL = '/static/' 100 | 101 | REST_FRAMEWORK = { 102 | 'DEFAULT_AUTHENTICATION_CLASSES': ( 103 | 'oauth2_provider.ext.rest_framework.OAuth2Authentication', 104 | ) 105 | } 106 | 107 | OAUTH2_PROVIDER = { 108 | # this is the list of available scopes 109 | 'SCOPES': {'read': 'Read scope', 'write': 'Write scope'} 110 | } 111 | 112 | SOCIAL_AUTH_FACEBOOK_KEY = # your facebook key 113 | SOCIAL_AUTH_FACEBOOK_SECRET = # your facebook secret 114 | -------------------------------------------------------------------------------- /beerstash/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | from django.contrib import admin 3 | 4 | urlpatterns = patterns( 5 | '', 6 | url('', include('social.apps.django_app.urls', namespace='social')), 7 | url(r'^beers/', include('beers.urls')), 8 | url(r'^admin/', include(admin.site.urls)), 9 | url(r'^users/', include('users.urls')), 10 | url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')), 11 | ) 12 | -------------------------------------------------------------------------------- /beerstash/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for beerstash project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "beerstash.settings") 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "beerstash.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.8 2 | django-braces==1.4.0 3 | django-oauth-toolkit==0.8.0 4 | djangorestframework==3.1.1 5 | oauthlib==0.7.2 6 | PyJWT==1.0.1 7 | python-openid==2.2.5 8 | python-social-auth==0.2.3 9 | requests==2.6.0 10 | requests-oauthlib==0.4.2 11 | six==1.9.0 12 | -------------------------------------------------------------------------------- /users/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felix-d/Django-Oauth-Toolkit-Python-Social-Auth-Integration/6645ea8e7e70b1fd0c61a35c8997288ec52fe5f7/users/__init__.py -------------------------------------------------------------------------------- /users/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /users/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felix-d/Django-Oauth-Toolkit-Python-Social-Auth-Integration/6645ea8e7e70b1fd0c61a35c8997288ec52fe5f7/users/migrations/__init__.py -------------------------------------------------------------------------------- /users/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | -------------------------------------------------------------------------------- /users/serializer.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from django.contrib.auth.models import User 3 | 4 | 5 | # first we define the serializers 6 | class UserSerializer(serializers.ModelSerializer): 7 | 8 | class Meta: 9 | model = User 10 | fields = ('id', 'username') 11 | -------------------------------------------------------------------------------- /users/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /users/tools.py: -------------------------------------------------------------------------------- 1 | from oauth2_provider.settings import oauth2_settings 2 | from oauthlib.common import generate_token 3 | from django.http import JsonResponse 4 | from oauth2_provider.models import AccessToken, Application, RefreshToken 5 | from django.utils.timezone import now, timedelta 6 | 7 | 8 | def get_token_json(access_token): 9 | """ 10 | Takes an AccessToken instance as an argument 11 | and returns a JsonResponse instance from that 12 | AccessToken 13 | """ 14 | token = { 15 | 'access_token': access_token.token, 16 | 'expires_in': oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS, 17 | 'token_type': 'Bearer', 18 | 'refresh_token': access_token.refresh_token.token, 19 | 'scope': access_token.scope 20 | } 21 | return JsonResponse(token) 22 | 23 | 24 | def get_access_token(user): 25 | """ 26 | Takes a user instance and return an access_token as a JsonResponse 27 | instance. 28 | """ 29 | 30 | # our oauth2 app 31 | app = Application.objects.get(name="myapp") 32 | 33 | # We delete the old access_token and refresh_token 34 | try: 35 | old_access_token = AccessToken.objects.get( 36 | user=user, application=app) 37 | old_refresh_token = RefreshToken.objects.get( 38 | user=user, access_token=old_access_token 39 | ) 40 | except: 41 | pass 42 | else: 43 | old_access_token.delete() 44 | old_refresh_token.delete() 45 | 46 | # we generate an access token 47 | token = generate_token() 48 | # we generate a refresh token 49 | refresh_token = generate_token() 50 | 51 | expires = now() + timedelta(seconds=oauth2_settings. 52 | ACCESS_TOKEN_EXPIRE_SECONDS) 53 | scope = "read write" 54 | 55 | # we create the access token 56 | access_token = AccessToken.objects.\ 57 | create(user=user, 58 | application=app, 59 | expires=expires, 60 | token=token, 61 | scope=scope) 62 | 63 | # we create the refresh token 64 | RefreshToken.objects.\ 65 | create(user=user, 66 | application=app, 67 | token=refresh_token, 68 | access_token=access_token) 69 | 70 | # we call get_token_json and returns the access token as json 71 | return get_token_json(access_token) 72 | -------------------------------------------------------------------------------- /users/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url, patterns 2 | from . import views 3 | 4 | 5 | urlpatterns = patterns( 6 | '', 7 | url(r'^register-by-token/(?P[^/]+)/$', 8 | views.register_by_access_token), 9 | ) 10 | -------------------------------------------------------------------------------- /users/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | from django.contrib.auth import login 3 | 4 | from social.apps.django_app.utils import psa 5 | 6 | from .tools import get_access_token 7 | 8 | 9 | @psa('social:complete') 10 | def register_by_access_token(request, backend): 11 | # This view expects an access_token GET parameter, if it's needed, 12 | # request.backend and request.strategy will be loaded with the current 13 | # backend and strategy. 14 | token = request.GET.get('access_token') 15 | user = request.backend.do_auth(token) 16 | if user: 17 | login(request, user) 18 | return get_access_token(user) 19 | else: 20 | return HttpResponse("error") 21 | --------------------------------------------------------------------------------