Skip to content

Commit

Permalink
fully optional jwt, test jwt
Browse files Browse the repository at this point in the history
  • Loading branch information
st4lk committed Dec 4, 2015
1 parent da96790 commit df4a0cc
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 44 deletions.
8 changes: 5 additions & 3 deletions example_project/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
</head>
<body>
<h1>Django-rest-framework and OAuth example</h1>
<a href="{% url 'home_session' %}">Session auth</a>
<a href="{% url 'home_token' %}">Token auth</a>
<a href="{% url 'home_jwt' %}">JWT auth</a>
<ul>
<li><a href="{% url 'home_session' %}">Session auth</a></li>
<li><a href="{% url 'home_token' %}">Token auth</a></li>
<li><a href="{% url 'home_jwt' %}">JWT auth</a></li>
</ul>

{% block content %}

Expand Down
5 changes: 5 additions & 0 deletions example_project/templates/home_jwt.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ <h2>Raw JWT payload</h2>
$auth.setToken(response.data.token);
set_user(response);
self.jwtPayload = $auth.getPayload();
}).catch(function(data) {
var err_msg = "Something went wrong, maybe you haven't installed 'djangorestframework-jwt'?";
console.log(data)
console.log(err_msg);
alert(err_msg);
});
};

Expand Down
8 changes: 4 additions & 4 deletions example_project/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_social_auth.serializers import UserSerializer
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_social_auth.serializers import UserSerializer
from rest_social_auth.views import JWTAuthMixin


class HomeSessionView(TemplateView):
Expand Down Expand Up @@ -51,5 +51,5 @@ class UserTokenDetailView(BaseDeatilView):
authentication_classes = (TokenAuthentication, )


class UserJWTDetailView(BaseDeatilView):
authentication_classes = (JSONWebTokenAuthentication, )
class UserJWTDetailView(JWTAuthMixin, BaseDeatilView):
pass
14 changes: 11 additions & 3 deletions rest_social_auth/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import logging
import warnings
from rest_framework import serializers
from rest_framework.authtoken.models import Token
from django.contrib.auth import get_user_model
Expand Down Expand Up @@ -44,17 +45,24 @@ def get_token(self, obj):
try:
from rest_framework_jwt.settings import api_settings
except ImportError:
l.warning("djangorestframework-jwt required for JWT authentication")
warnings.warn('djangorestframework-jwt must be installed for JWT autthentication', ImportWarning)
raise

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(obj) # obj is the user instance

payload = jwt_payload_handler(self.get_jwt_payload(obj))
token = jwt_encode_handler(payload)

return token

def get_jwt_payload(self, obj):
"""
Define here, what data shall be encoded in JWT.
By default, entire object will be encoded.
"""
return obj


class UserJWTSerializer(JWTSerializer, UserSerializer):
pass
24 changes: 14 additions & 10 deletions rest_social_auth/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import logging
import warnings
try:
from urlparse import urlparse
except ImportError:
Expand Down Expand Up @@ -28,12 +29,6 @@

l = logging.getLogger(__name__)

try:
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
except ImportError:
# this is only needed for JWT auth
l.warning("djangorestframework-jwt required for JWT autthentication")


REDIRECT_URI = getattr(settings, 'REST_SOCIAL_OAUTH_REDIRECT_URI', '/')
DOMAIN_FROM_ORIGIN = getattr(settings, 'REST_SOCIAL_DOMAIN_FROM_ORIGIN', True)
Expand Down Expand Up @@ -197,11 +192,20 @@ class SocialTokenUserAuthView(BaseSocialAuthView):
authentication_classes = (TokenAuthentication, )


class SocialJWTOnlyAuthView(BaseSocialAuthView):
class JWTAuthMixin(object):
def get_authenticators(self):
try:
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
except ImportError:
warnings.warn('djangorestframework-jwt must be installed for JWT autthentication', ImportWarning)
raise

return [JSONWebTokenAuthentication()]


class SocialJWTOnlyAuthView(JWTAuthMixin, BaseSocialAuthView):
serializer_class = JWTSerializer
authentication_classes = (JSONWebTokenAuthentication, )


class SocialJWTUserAuthView(BaseSocialAuthView):
class SocialJWTUserAuthView(JWTAuthMixin, BaseSocialAuthView):
serializer_class = UserJWTSerializer
authentication_classes = (JSONWebTokenAuthentication, )
139 changes: 115 additions & 24 deletions tests/test_social.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,47 @@
delattr(TwitterOAuth1Test, attr)


session_modify_settings = dict(
INSTALLED_APPS={
'remove': [
'rest_framework.authtoken',
]
},
)


token_modify_settings = dict(
INSTALLED_APPS={
'remove': [
'django.contrib.sessions'
]
},
MIDDLEWARE_CLASSES={
'remove': [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
],
}
)


jwt_modify_settings = dict(
INSTALLED_APPS={
'remove': [
'django.contrib.sessions',
'rest_framework.authtoken',
]
},
MIDDLEWARE_CLASSES={
'remove': [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
],
}
)

class RestSocialMixin(object):
def setUp(self):
HTTPretty.enable()
Expand Down Expand Up @@ -76,6 +117,7 @@ def do_rest_login(self):

class TestSocialAuth1(APITestCase, BaseTiwtterApiTestCase):

@modify_settings(**session_modify_settings)
def test_login_social_oauth1_session(self):
resp = self.client.post(reverse('login_social_session'),
data={'provider': 'twitter'})
Expand Down Expand Up @@ -105,9 +147,33 @@ def test_login_social_oauth1_token(self):
})
self.assertEqual(resp.status_code, 200)

@modify_settings(INSTALLED_APPS={'remove': ['rest_framework.authtoken', ]})
def test_login_social_oauth1_jwt(self):
"""
Currently oauth1 works only if session is enabled.
Probably it is possible to make it work without session, but
it will be needed to change the logic in python-social-auth.
"""
try:
import rest_framework_jwt
except ImportError:
return
assert rest_framework_jwt is not None
resp = self.client.post(reverse('login_social_jwt_user'),
data={'provider': 'twitter'})
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.data, parse_qs(self.request_token_body))
resp = self.client.post(reverse('login_social_token_user'), data={
'provider': 'twitter',
'oauth_token': 'foobar',
'oauth_verifier': 'overifier'
})
self.assertEqual(resp.status_code, 200)


class TestSocialAuth2(APITestCase, BaseFacebookAPITestCase):

@modify_settings(**session_modify_settings)
def _check_login_social_session(self, url, data):
resp = self.client.post(url, data)
self.assertEqual(resp.status_code, 200)
Expand All @@ -118,18 +184,7 @@ def _check_login_social_session(self, url, data):
self.assertTrue(
get_user_model().objects.filter(email=self.email).exists())

@modify_settings(INSTALLED_APPS={
'remove': [
'django.contrib.sessions'
]
},
MIDDLEWARE_CLASSES={
'remove': [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
],
})
@modify_settings(**token_modify_settings)
def _check_login_social_token_user(self, url, data):
resp = self.client.post(url, data)
self.assertEqual(resp.status_code, 200)
Expand All @@ -139,18 +194,7 @@ def _check_login_social_token_user(self, url, data):
# check user is created
self.assertEqual(token.user.email, self.email)

@modify_settings(INSTALLED_APPS={
'remove': [
'django.contrib.sessions'
]
},
MIDDLEWARE_CLASSES={
'remove': [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
],
})
@modify_settings(**token_modify_settings)
def _check_login_social_token_only(self, url, data):
resp = self.client.post(url, data)
self.assertEqual(resp.status_code, 200)
Expand All @@ -159,6 +203,33 @@ def _check_login_social_token_only(self, url, data):
# check user is created
self.assertEqual(token.user.email, self.email)

@modify_settings(**jwt_modify_settings)
def _check_login_social_jwt_only(self, url, data):
try:
from rest_framework_jwt.settings import api_settings
except ImportError:
return
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
resp = self.client.post(url, data)
self.assertEqual(resp.status_code, 200)
# check token valid
jwt_data = jwt_decode_handler(resp.data['token'])
self.assertEqual(jwt_data['email'], self.email)

@modify_settings(**jwt_modify_settings)
def _check_login_social_jwt_user(self, url, data):
try:
from rest_framework_jwt.settings import api_settings
except ImportError:
return
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
resp = self.client.post(url, data)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.data['email'], self.email)
# check token valid
jwt_data = jwt_decode_handler(resp.data['token'])
self.assertEqual(jwt_data['email'], self.email)

def test_login_social_session(self):
self._check_login_social_session(
reverse('login_social_session'),
Expand Down Expand Up @@ -189,6 +260,26 @@ def test_login_social_token_only_provider_in_url(self):
reverse('login_social_token', kwargs={'provider': 'facebook'}),
data={'code': '3D52VoM1uiw94a1ETnGvYlCw'})

def test_login_social_jwt_only(self):
self._check_login_social_jwt_only(
reverse('login_social_jwt'),
data={'provider': 'facebook', 'code': '3D52VoM1uiw94a1ETnGvYlCw'})

def test_login_social_jwt_only_provider_in_url(self):
self._check_login_social_jwt_only(
reverse('login_social_jwt', kwargs={'provider': 'facebook'}),
data={'code': '3D52VoM1uiw94a1ETnGvYlCw'})

def test_login_social_jwt_user(self):
self._check_login_social_jwt_user(
reverse('login_social_jwt_user'),
data={'provider': 'facebook', 'code': '3D52VoM1uiw94a1ETnGvYlCw'})

def test_login_social_jwt_user_provider_in_url(self):
self._check_login_social_jwt_user(
reverse('login_social_jwt_user', kwargs={'provider': 'facebook'}),
data={'code': '3D52VoM1uiw94a1ETnGvYlCw'})

def test_no_provider_session(self):
resp = self.client.post(
reverse('login_social_session'),
Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ deps =
basepython = python2.7
deps =
django<1.9
djangorestframework-jwt
coverage
{[testenv]deps}
commands =
Expand Down

0 comments on commit df4a0cc

Please sign in to comment.