Skip to content
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

Add login/session integration, and an auth middleware #16

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ Now in order to access protected api urls you must include the `Authorization: B
$ curl -H "Authorization: Bearer <your_token>" http://localhost:8000/protected-url/
```

There is also a provided middleware if you would prefer that to the view integration. Just add the following to your middleware:

```python
MIDDLEWARE_CLASSES = (
# ...
'jwt_auth.middleware.JWTAuthenticationMiddleware',
)
```

## Additional Settings
There are some additional settings that you can override similar to how you'd do it with Django REST framework itself. Here are all the available defaults.

Expand All @@ -58,6 +67,7 @@ JWT_EXPIRATION_DELTA = datetime.timedelta(seconds=300)
JWT_ALLOW_REFRESH = False
JWT_REFRESH_EXPIRATION_DELTA = datetime.timedelta(days=7)
JWT_AUTH_HEADER_PREFIX = 'Bearer'
JWT_LOGIN_INTEGRATION = False
```
This packages uses the JSON Web Token Python implementation, [PyJWT](https://github.com/progrium/pyjwt) and allows to modify some of it's available options.

Expand Down Expand Up @@ -126,6 +136,8 @@ You can modify the Authorization header value prefix that is required to be sent

Default is `Bearer`.

### JWT_LOGIN_INTEGRATION
Creates and stores a JWT token in your session when you successfully login. This token is accessible from the `jwt_token` template tag for putting in an HTML meta header.

[build-status-image]: https://secure.travis-ci.org/jpadilla/django-jwt-auth.svg?branch=master
[travis]: http://travis-ci.org/jpadilla/django-jwt-auth?branch=master
Expand Down
2 changes: 2 additions & 0 deletions jwt_auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@
__author__ = 'Jose Padilla'
__license__ = 'MIT'
__copyright__ = 'Copyright 2014 Jose Padilla'

default_app_config = 'jwt_auth.apps.JwtAuthAppConfig'
17 changes: 17 additions & 0 deletions jwt_auth/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django.apps import AppConfig
from django.contrib.auth.signals import user_logged_in
from jwt_auth import settings


class JwtAuthAppConfig(AppConfig):
name = 'jwt_auth'
verbose_name = 'JwtAuth'

def ready(self):
super(JwtAuthAppConfig, self).ready()
if settings.JWT_LOGIN_INTEGRATION:
from . import signals

user_logged_in.connect(
signals.logged_in,
dispatch_uid="user_logged_in")
68 changes: 68 additions & 0 deletions jwt_auth/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import jwt
from jwt_auth import settings, exceptions
from jwt_auth.utils import get_authorization_header
from jwt_auth.compat import json, smart_text, User

import logging
logger = logging.getLogger(__name__)

jwt_decode_handler = settings.JWT_DECODE_HANDLER
jwt_get_user_id_from_payload = settings.JWT_PAYLOAD_GET_USER_ID_HANDLER


class JWTAuthenticationMiddleware(object):
"""
Token based authentication using the JSON Web Token standard.

Clients should authenticate by passing the token key in the "Authorization"
HTTP header, prepended with the string specified in the setting
`JWT_AUTH_HEADER_PREFIX`. For example:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
"""

def __init__(self, get_response=None):
self.get_response = get_response

def process_request(self, request):
try:
auth = get_authorization_header(request).split()
auth_header_prefix = settings.JWT_AUTH_HEADER_PREFIX.lower()

if not auth or smart_text(auth[0].lower()) != auth_header_prefix:
raise exceptions.AuthenticationFailed()

if len(auth) == 1:
msg = 'Invalid Authorization header. No credentials provided.'
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = ('Invalid Authorization header. Credentials string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)

try:
payload = jwt_decode_handler(auth[1])
except jwt.ExpiredSignature:
msg = 'Signature has expired.'
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = 'Error decoding signature.'
raise exceptions.AuthenticationFailed(msg)

try:
user_id = jwt_get_user_id_from_payload(payload)

if user_id:
user = User.objects.get(pk=user_id, is_active=True)
else:
msg = 'Invalid payload'
raise exceptions.AuthenticationFailed(msg)
except User.DoesNotExist:
msg = 'Invalid signature'
raise exceptions.AuthenticationFailed(msg)

request.user = user
except exceptions.AuthenticationFailed as e:
logger.exception(e)

def process_response(self, request, response):
return response
2 changes: 2 additions & 0 deletions jwt_auth/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,5 @@
)

JWT_AUTH_HEADER_PREFIX = getattr(settings, 'JWT_AUTH_HEADER_PREFIX', 'Bearer')

JWT_LOGIN_INTEGRATION = getattr(settings, 'JWT_LOGIN_INTEGRATION', False)
28 changes: 28 additions & 0 deletions jwt_auth/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
Signal handlers for jwt_auth app
"""
from calendar import timegm
from datetime import datetime

from jwt_auth import settings


jwt_payload_handler = settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = settings.JWT_ENCODE_HANDLER


def logged_in(sender, request, user, **kwargs):
"""
Put JWT token in session
"""

payload = jwt_payload_handler(user)

# Include original issued at time for a brand new token,
# to allow token refresh
if settings.JWT_ALLOW_REFRESH:
payload['orig_iat'] = timegm(
datetime.utcnow().utctimetuple()
)

request.session['jwt_token'] = jwt_encode_handler(payload)
Empty file.
8 changes: 8 additions & 0 deletions jwt_auth/templatetags/jwt_tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django import template

register = template.Library()

@register.simple_tag(takes_context=True)
def jwt_token(context):
request = context['request']
return request.session.get('jwt_token', '')