Skip to content

Commit

Permalink
Finalize session simulation for OAuth1 (#118)
Browse files Browse the repository at this point in the history
* Finalize session simulation for OAuth1
  • Loading branch information
st4lk committed Feb 9, 2020
1 parent 1091932 commit 87e5422
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 93 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@ native-run-example: native-migrate
native-clean:
find . -path ./venv -prune | grep -E "(__pycache__|\.pyc|\.pyo$$)" | xargs rm -rf

native-test: native-install-all native-clean native-lint
native-test-only: native-install-all native-clean
PYTHONPATH='example_project/' python -m pytest -Wignore $(TEST_ARGS)

native-test: native-lint native-test-only

native-lint: native-install-all
flake8 .

Expand Down
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,10 +330,6 @@ OAuth 1.0a workflow with rest-social-auth

This flow is the same as described in [satellizer](https://github.com/sahat/satellizer#-login-with-oauth-10). This angularjs module is used in example project.

#### Note
If you use token (or jwt) authentication and OAuth 1.0, then you still need 'django.contrib.sessions' app (it is not required for OAuth 2.0 and token authentication).
This is because python-social-auth will store some data in session between requests to OAuth 1.0 provider.


rest-social-auth purpose
------------------------
Expand Down
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ rest_social_auth release notes
master
------
- Update supported version of django-rest-knox (>=4.0.0, <5.0.0). v4 has breaking changes.
- Allow to use token auth with OAuth1 without django session enabled

Issues: #110

Expand Down
33 changes: 22 additions & 11 deletions rest_social_auth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,21 +166,32 @@ def get_object(self):
self.request.backend.STATE_PARAMETER = False

if self.oauth_v1():
# Oauth1 uses sessions, in case of token authentication session store will be empty
backend = self.request.backend
session_token_name = backend.name + backend.UNATHORIZED_TOKEN_SUFIX
if not self.request.session.exists(session_token_name):
oauth1_tokrn_param = backend.data.get(backend.OAUTH_TOKEN_PARAMETER_NAME)
self.request.session[session_token_name] = [
urlencode({
backend.OAUTH_TOKEN_PARAMETER_NAME: oauth1_tokrn_param,
'oauth_token_secret': backend.data.get('oauth_token_secret')
})
]
self.save_token_param_in_session()

user = self.request.backend.complete(user=user)
return user

def save_token_param_in_session(self):
"""
Save token param in strategy's session.
This method will allow to use token auth with OAuth1 even if session is not enabled in
django settings (social_core expects that session is enabled).
"""
backend = self.request.backend
session_token_name = backend.name + backend.UNATHORIZED_TOKEN_SUFIX
session = self.request.strategy.session
if (
(isinstance(session, dict) and session_token_name not in session) or
not session.exists(session_token_name)
):
oauth1_token_param = backend.data.get(backend.OAUTH_TOKEN_PARAMETER_NAME)
session[session_token_name] = [
urlencode({
backend.OAUTH_TOKEN_PARAMETER_NAME: oauth1_token_param,
'oauth_token_secret': backend.data.get('oauth_token_secret')
})
]

def do_login(self, backend, user):
"""
Do login action here.
Expand Down
36 changes: 12 additions & 24 deletions tests/test_jwt.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.test import modify_settings
from django.test import override_settings
from django.urls import reverse
from rest_framework.test import APITestCase
from rest_framework_jwt.settings import api_settings as jwt_api_settings
Expand All @@ -7,33 +7,22 @@
from .base import BaseFacebookAPITestCase, BaseTwitterApiTestCase


jwt_modify_settings = dict(
INSTALLED_APPS={
'remove': [
'django.contrib.sessions',
'rest_framework.authtoken',
'knox',
]
},
MIDDLEWARE_CLASSES={
'remove': [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
],
}
jwt_override_settings = dict(
INSTALLED_APPS=[
'django.contrib.contenttypes',
'rest_framework',
'social_django',
'rest_social_auth',
'users',
],
MIDDLEWARE=[],
)


@override_settings(**jwt_override_settings)
class TestSocialAuth1JWT(APITestCase, BaseTwitterApiTestCase):

@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.
"""
resp = self.client.post(
reverse('login_social_jwt_user'), data={'provider': 'twitter'})
self.assertEqual(resp.status_code, 200)
Expand All @@ -46,9 +35,9 @@ def test_login_social_oauth1_jwt(self):
self.assertEqual(resp.status_code, 200)


@override_settings(**jwt_override_settings)
class TestSocialAuth2JWT(APITestCase, BaseFacebookAPITestCase):

@modify_settings(**jwt_modify_settings)
def _check_login_social_jwt_only(self, url, data):
jwt_decode_handler = jwt_api_settings.JWT_DECODE_HANDLER
resp = self.client.post(url, data)
Expand All @@ -57,7 +46,6 @@ def _check_login_social_jwt_only(self, url, data):
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):
jwt_decode_handler = jwt_api_settings.JWT_DECODE_HANDLER
resp = self.client.post(url, data)
Expand Down
22 changes: 17 additions & 5 deletions tests/test_knox.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.test import override_settings
from django.urls import reverse
from knox.auth import TokenAuthentication as KnoxTokenAuthentication
from rest_framework.test import APITestCase
Expand All @@ -6,14 +7,24 @@
from .base import BaseFacebookAPITestCase, BaseTwitterApiTestCase


knox_override_settings = dict(
INSTALLED_APPS=[
'django.contrib.contenttypes',
'rest_framework',
'social_django',
'rest_social_auth',
'knox', # For django-rest-knox
'users',
],
MIDDLEWARE=[
],
)


@override_settings(**knox_override_settings)
class TestSocialAuth1Knox(APITestCase, BaseTwitterApiTestCase):

def test_login_social_oauth1_knox(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.
"""
resp = self.client.post(
reverse('login_social_knox_user'), data={'provider': 'twitter'})
self.assertEqual(resp.status_code, 200)
Expand All @@ -26,6 +37,7 @@ def test_login_social_oauth1_knox(self):
self.assertEqual(resp.status_code, 200)


@override_settings(**knox_override_settings)
class TestSocialAuth2Knox(APITestCase, BaseFacebookAPITestCase):

def _check_login_social_knox_only(self, url, data):
Expand Down
36 changes: 12 additions & 24 deletions tests/test_simple_jwt.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.test import modify_settings
from django.test import override_settings
from django.urls import reverse
from rest_framework.test import APITestCase
from rest_framework_simplejwt.authentication import JWTAuthentication
Expand All @@ -7,33 +7,22 @@
from .base import BaseFacebookAPITestCase, BaseTwitterApiTestCase


jwt_simple_modify_settings = dict(
INSTALLED_APPS={
'remove': [
'django.contrib.sessions',
'rest_framework.authtoken',
'knox',
]
},
MIDDLEWARE_CLASSES={
'remove': [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
],
}
jwt_simple_override_settings = dict(
INSTALLED_APPS=[
'django.contrib.contenttypes',
'rest_framework',
'social_django',
'rest_social_auth',
'users',
],
MIDDLEWARE=[],
)


@override_settings(**jwt_simple_override_settings)
class TestSocialAuth1SimpleJWT(APITestCase, BaseTwitterApiTestCase):

@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.
"""
resp = self.client.post(
reverse('login_social_jwt_user'), data={'provider': 'twitter'})
self.assertEqual(resp.status_code, 200)
Expand All @@ -46,9 +35,9 @@ def test_login_social_oauth1_jwt(self):
self.assertEqual(resp.status_code, 200)


@override_settings(**jwt_simple_override_settings)
class TestSocialAuth2SimpleJWT(APITestCase, BaseFacebookAPITestCase):

@modify_settings(**jwt_simple_modify_settings)
def _check_login_social_simple_jwt_only(self, url, data, token_type):
resp = self.client.post(url, data)
self.assertEqual(resp.status_code, 200)
Expand All @@ -57,7 +46,6 @@ def _check_login_social_simple_jwt_only(self, url, data, token_type):
token_instance = jwt_auth.get_validated_token(resp.data['token'])
self.assertEqual(token_instance['token_type'], token_type)

@modify_settings(**jwt_simple_modify_settings)
def _check_login_social_simple_jwt_user(self, url, data, token_type):
resp = self.client.post(url, data)
self.assertEqual(resp.status_code, 200)
Expand Down
39 changes: 15 additions & 24 deletions tests/test_token.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json

from django.test import modify_settings
from django.test import override_settings
from django.urls import reverse
from rest_framework.authtoken.models import Token
from rest_framework.test import APITestCase
Expand All @@ -9,33 +9,25 @@
from .base import BaseFacebookAPITestCase, BaseTwitterApiTestCase


token_modify_settings = dict(
INSTALLED_APPS={
'remove': [
'django.contrib.sessions',
'knox',
]
},
MIDDLEWARE_CLASSES={
'remove': [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
],
}
token_override_settings = dict(
INSTALLED_APPS=[
'django.contrib.contenttypes',
'rest_framework',
'rest_framework.authtoken',
'social_django',
'rest_social_auth',
'users',
],
MIDDLEWARE=[],
)


@override_settings(**token_override_settings)
class TestSocialAuth1Token(APITestCase, BaseTwitterApiTestCase):

def test_login_social_oauth1_token(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.
"""
resp = self.client.post(
reverse('login_social_token_user'), data={'provider': 'twitter'})
url = reverse('login_social_token_user')
resp = self.client.post(url, 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={
Expand All @@ -46,9 +38,9 @@ def test_login_social_oauth1_token(self):
self.assertEqual(resp.status_code, 200)


@override_settings(**token_override_settings)
class TestSocialAuth2Token(APITestCase, BaseFacebookAPITestCase):

@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 @@ -58,7 +50,6 @@ def _check_login_social_token_user(self, url, data):
# check user is created
self.assertEqual(token.user.email, self.email)

@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 Down

0 comments on commit 87e5422

Please sign in to comment.