Skip to content
This repository was archived by the owner on Dec 6, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
2 changes: 1 addition & 1 deletion Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ FROM python:3.6

WORKDIR /code
RUN pip install tox
ADD . /code
ADD . /code

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is not necessary. Every file should end in a new line

10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,16 @@ hTZAZmJhid2o/+ya/28muuoQgknEoJz32bKeWuYZrFkRKUrGFnlxHwIDAQAB
"""
```

By default authentication will be enabled, use `JWT_AUTH_DISABLED` setting variable to disable that feature:

```
#settings.py

# Default JWT_AUTH_DISABLED=False
JWT_AUTH_DISABLED=True

```


Local development
=================
Expand Down
2 changes: 1 addition & 1 deletion docker-compose-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ services:
container_name: dot_jwt
image: dot_jwt
volumes:
- .:/code
- .:/code
Copy link

@rafa-munoz rafa-munoz Jul 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is not necessary. Every file should end in a new line

31 changes: 30 additions & 1 deletion oauth2_provider_jwt/authentication.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.contrib.auth import get_user_model
from django.utils.encoding import smart_text
import jwt
from rest_framework import exceptions
Expand Down Expand Up @@ -43,7 +44,35 @@ def authenticate(self, request):
raise exceptions.AuthenticationFailed()

self._add_session_details(request, payload)
return AnonymousUser(), payload

user = self.authenticate_credentials(payload)
return user, payload

def authenticate_credentials(self, payload):
"""
Returns an active user that matches the payload's user id and email.
"""
if getattr(settings, 'JWT_AUTH_DISABLED', False):
return AnonymousUser()

User = get_user_model()
username = payload.get('username')

if not username:
msg = 'Invalid payload.'
raise exceptions.AuthenticationFailed(msg)

try:
user = User.objects.get_by_natural_key(username)
except User.DoesNotExist:
msg = 'Invalid signature.'
raise exceptions.AuthenticationFailed(msg)

if not user.is_active:
msg = 'User account is disabled.'
raise exceptions.AuthenticationFailed(msg)

return user

def _get_jwt_value(self, request):
auth = get_authorization_header(request).split()
Expand Down
1 change: 1 addition & 0 deletions oauth2_provider_jwt/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def _get_access_token_jwt(self, request, expires_in):
if payload_enricher:
fn = import_string(payload_enricher)
extra_data = fn(request)
extra_data['username'] = request.POST.get('username')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every request won't have a username. For example on refresh_token actions. It should not be inside this if payload_enricher and something like:

if request.POST.get('username'):
    extra_data['username'] = request.POST.get('username')

payload = generate_payload(issuer, expires_in, **extra_data)
token = encode_jwt(payload)
return token
Expand Down
22 changes: 22 additions & 0 deletions tests/test_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json

from django.test import TestCase
from django.contrib.auth import get_user_model
from rest_framework.test import APIClient

from oauth2_provider_jwt import utils
Expand All @@ -10,6 +11,9 @@
class JWTAuthenticationTests(TestCase):
def setUp(self):
self.client = APIClient(enforce_csrf_checks=True)
User = get_user_model()
User.objects.create_user(
'temporary', '[email protected]', 'temporary')

def test_get_no_jwt_header(self):
"""
Expand Down Expand Up @@ -66,3 +70,21 @@ def test_post_valid_jwt_header(self):
if k not in ('exp', 'iat'):
sessionkeys_expected['jwt_{}'.format(k)] = v
self.assertEqual(json.loads(response.content), sessionkeys_expected)

def test_post_valid_jwt_with_auth(self):
now = datetime.utcnow()
payload = {
'iss': 'issuer',
'exp': now + timedelta(seconds=100),
'iat': now,
'username': 'temporary',
}
jwt_value = utils.encode_jwt(payload)

response = self.client.post(
'/jwt_auth/', {'example': 'example'},
HTTP_AUTHORIZATION='JWT {}'.format(jwt_value),
content_type='application/json')
self.assertEqual(response.status_code, 200)
self.assertEqual(
json.loads(response.content), {'username': 'temporary'})
12 changes: 12 additions & 0 deletions tests/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,22 @@ def post(self, request):
return HttpResponse(response)


class MockForAuthView(APIView):
permission_classes = (permissions.IsAuthenticated,)

def get(self, _request):
return HttpResponse('mockforauthview-get')

def post(self, request):
response = json.dumps({"username": request.user.username})
return HttpResponse(response)


urlpatterns = [
url(r"^o/", include("oauth2_provider_jwt.urls",
namespace="oauth2_provider_jwt")),
url(r'^jwt/$', MockView.as_view()),
url(r'^jwt_auth/$', MockForAuthView.as_view()),
]


Expand Down