diff --git a/core/auth.py b/core/auth.py index f37fe4f..d656ea7 100644 --- a/core/auth.py +++ b/core/auth.py @@ -1,17 +1,22 @@ +import jwt from django.conf import settings +from django.contrib.auth import get_user_model from django.utils.encoding import smart_str from django.utils.translation import gettext as _ from rest_framework import HTTP_HEADER_ENCODING, exceptions -from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication +from rest_framework.authentication import BaseAuthentication from rest_framework_jwt.settings import api_settings from six import text_type +jwt_decode_handler = api_settings.JWT_DECODE_HANDLER +jwt_get_username_from_payload = api_settings.JWT_PAYLOAD_GET_USERNAME_HANDLER -class JSONWebTokenAuthenticationQS(BaseJSONWebTokenAuthentication): + +class JSONWebTokenAuthenticationQS(BaseAuthentication): """ - This is a custom JWT Authentication class. This has inherited - BaseJsonWebTokenAuthentication and also used some of the codes from - traditional JSONWebTokenAuthentication class. The traditional one + Token based authentication using the JSON Web Token standard. + + This is a custom JWT Authentication class. The traditional one can only authenticate from Header with a specific key only. This model will first look into HEADER and if the key is not found @@ -92,3 +97,50 @@ def get_jwt_value(self, request): raise exceptions.AuthenticationFailed(msg) return auth[1] + + def authenticate(self, request): + """ + Returns a two-tuple of `User` and token if a valid signature has been + supplied using JWT-based authentication. Otherwise returns `None`. + """ + jwt_value = self.get_jwt_value(request) + if jwt_value is None: + return None + + try: + payload = jwt_decode_handler(jwt_value) + except jwt.ExpiredSignature: + msg = _("Signature has expired.") + raise exceptions.AuthenticationFailed(msg) + except jwt.DecodeError: + msg = _("Error decoding signature.") + raise exceptions.AuthenticationFailed(msg) + except jwt.InvalidTokenError: + raise exceptions.AuthenticationFailed() + + user = self.authenticate_credentials(payload) + + return (user, jwt_value) + + def authenticate_credentials(self, payload): + """ + Returns an active user that matches the payload's user id and email. + """ + User = get_user_model() + username = jwt_get_username_from_payload(payload) + + 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