mixins.py 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. from django.http import HttpResponse
  2. import jwt
  3. from django.utils.decorators import method_decorator
  4. from django.views.decorators.csrf import csrf_exempt
  5. from jwt_auth import settings, exceptions
  6. from jwt_auth.utils import get_authorization_header
  7. from jwt_auth.compat import json, smart_text, User
  8. jwt_decode_handler = settings.JWT_DECODE_HANDLER
  9. jwt_get_user_id_from_payload = settings.JWT_PAYLOAD_GET_USER_ID_HANDLER
  10. class JSONWebTokenAuthMixin(object):
  11. """
  12. Token based authentication using the JSON Web Token standard.
  13. Clients should authenticate by passing the token key in the "Authorization"
  14. HTTP header, prepended with the string specified in the setting
  15. `JWT_AUTH_HEADER_PREFIX`. For example:
  16. Authorization: JWT eyJhbGciOiAiSFMyNTYiLCAidHlwIj
  17. """
  18. www_authenticate_realm = 'api'
  19. @method_decorator(csrf_exempt)
  20. def dispatch(self, request, *args, **kwargs):
  21. try:
  22. request.user, request.token = self.authenticate(request)
  23. except exceptions.AuthenticationFailed as e:
  24. response = HttpResponse(
  25. json.dumps({'errors': [str(e)]}),
  26. status=401,
  27. content_type='application/json'
  28. )
  29. response['WWW-Authenticate'] = self.authenticate_header(request)
  30. return response
  31. return super(JSONWebTokenAuthMixin, self).dispatch(
  32. request, *args, **kwargs)
  33. def authenticate(self, request):
  34. auth = get_authorization_header(request).split()
  35. auth_header_prefix = settings.JWT_AUTH_HEADER_PREFIX.lower()
  36. if not auth or smart_text(auth[0].lower()) != auth_header_prefix:
  37. raise exceptions.AuthenticationFailed()
  38. if len(auth) == 1:
  39. msg = 'Invalid Authorization header. No credentials provided.'
  40. raise exceptions.AuthenticationFailed(msg)
  41. elif len(auth) > 2:
  42. msg = ('Invalid Authorization header. Credentials string '
  43. 'should not contain spaces.')
  44. raise exceptions.AuthenticationFailed(msg)
  45. try:
  46. payload = jwt_decode_handler(auth[1])
  47. except jwt.ExpiredSignature:
  48. msg = 'Signature has expired.'
  49. raise exceptions.AuthenticationFailed(msg)
  50. except jwt.DecodeError:
  51. msg = 'Error decoding signature.'
  52. raise exceptions.AuthenticationFailed(msg)
  53. user = self.authenticate_credentials(payload)
  54. return (user, auth[1])
  55. def authenticate_credentials(self, payload):
  56. """
  57. Returns an active user that matches the payload's user id and email.
  58. """
  59. try:
  60. user_id = jwt_get_user_id_from_payload(payload)
  61. if user_id:
  62. user = User.objects.get(pk=user_id, is_active=True)
  63. else:
  64. msg = 'Invalid payload'
  65. raise exceptions.AuthenticationFailed(msg)
  66. except User.DoesNotExist:
  67. msg = 'Invalid signature'
  68. raise exceptions.AuthenticationFailed(msg)
  69. return user
  70. def authenticate_header(self, request):
  71. """
  72. Return a string to be used as the value of the `WWW-Authenticate`
  73. header in a `401 Unauthenticated` response, or `None` if the
  74. authentication scheme should return `403 Permission Denied` responses.
  75. """
  76. return 'JWT realm="{0}"'.format(self.www_authenticate_realm)