authentication.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. from django.contrib.auth import get_user_model
  2. from django.utils.translation import gettext_lazy as _
  3. from rest_framework import HTTP_HEADER_ENCODING, authentication
  4. from .exceptions import AuthenticationFailed, InvalidToken, TokenError
  5. from .settings import api_settings
  6. AUTH_HEADER_TYPES = api_settings.AUTH_HEADER_TYPES
  7. if not isinstance(api_settings.AUTH_HEADER_TYPES, (list, tuple)):
  8. AUTH_HEADER_TYPES = (AUTH_HEADER_TYPES,)
  9. AUTH_HEADER_TYPE_BYTES = set(
  10. h.encode(HTTP_HEADER_ENCODING)
  11. for h in AUTH_HEADER_TYPES
  12. )
  13. class JWTAuthentication(authentication.BaseAuthentication):
  14. """
  15. An authentication plugin that authenticates requests through a JSON web
  16. token provided in a request header.
  17. """
  18. www_authenticate_realm = 'api'
  19. media_type = 'application/json'
  20. def __init__(self, *args, **kwargs):
  21. super().__init__(*args, **kwargs)
  22. self.user_model = get_user_model()
  23. def authenticate(self, request):
  24. header = self.get_header(request)
  25. if header is None:
  26. return None
  27. raw_token = self.get_raw_token(header)
  28. if raw_token is None:
  29. return None
  30. validated_token = self.get_validated_token(raw_token)
  31. return self.get_user(validated_token), validated_token
  32. def authenticate_header(self, request):
  33. return '{0} realm="{1}"'.format(
  34. AUTH_HEADER_TYPES[0],
  35. self.www_authenticate_realm,
  36. )
  37. def get_header(self, request):
  38. """
  39. Extracts the header containing the JSON web token from the given
  40. request.
  41. """
  42. header = request.META.get(api_settings.AUTH_HEADER_NAME)
  43. if isinstance(header, str):
  44. # Work around django test client oddness
  45. header = header.encode(HTTP_HEADER_ENCODING)
  46. return header
  47. def get_raw_token(self, header):
  48. """
  49. Extracts an unvalidated JSON web token from the given "Authorization"
  50. header value.
  51. """
  52. parts = header.split()
  53. if len(parts) == 0:
  54. # Empty AUTHORIZATION header sent
  55. return None
  56. if parts[0] not in AUTH_HEADER_TYPE_BYTES:
  57. # Assume the header does not contain a JSON web token
  58. return None
  59. if len(parts) != 2:
  60. raise AuthenticationFailed(
  61. _('Authorization header must contain two space-delimited values'),
  62. code='bad_authorization_header',
  63. )
  64. return parts[1]
  65. def get_validated_token(self, raw_token):
  66. """
  67. Validates an encoded JSON web token and returns a validated token
  68. wrapper object.
  69. """
  70. messages = []
  71. for AuthToken in api_settings.AUTH_TOKEN_CLASSES:
  72. try:
  73. return AuthToken(raw_token)
  74. except TokenError as e:
  75. messages.append({'token_class': AuthToken.__name__,
  76. 'token_type': AuthToken.token_type,
  77. 'message': e.args[0]})
  78. raise InvalidToken({
  79. 'detail': _('Given token not valid for any token type'),
  80. 'messages': messages,
  81. })
  82. def get_user(self, validated_token):
  83. """
  84. Attempts to find and return a user using the given validated token.
  85. """
  86. try:
  87. user_id = validated_token[api_settings.USER_ID_CLAIM]
  88. except KeyError:
  89. raise InvalidToken(_('Token contained no recognizable user identification'))
  90. try:
  91. user = self.user_model.objects.get(**{api_settings.USER_ID_FIELD: user_id})
  92. except self.user_model.DoesNotExist:
  93. raise AuthenticationFailed(_('User not found'), code='user_not_found')
  94. if not user.is_active:
  95. raise AuthenticationFailed(_('User is inactive'), code='user_inactive')
  96. return user
  97. class JWTTokenUserAuthentication(JWTAuthentication):
  98. def get_user(self, validated_token):
  99. """
  100. Returns a stateless user object which is backed by the given validated
  101. token.
  102. """
  103. if api_settings.USER_ID_CLAIM not in validated_token:
  104. # The TokenUser class assumes tokens will have a recognizable user
  105. # identifier claim.
  106. raise InvalidToken(_('Token contained no recognizable user identification'))
  107. return api_settings.TOKEN_USER_CLASS(validated_token)
  108. def default_user_authentication_rule(user):
  109. # Prior to Django 1.10, inactive users could be authenticated with the
  110. # default `ModelBackend`. As of Django 1.10, the `ModelBackend`
  111. # prevents inactive users from authenticating. App designers can still
  112. # allow inactive users to authenticate by opting for the new
  113. # `AllowAllUsersModelBackend`. However, we explicitly prevent inactive
  114. # users from authenticating to enforce a reasonable policy and provide
  115. # sensible backwards compatibility with older Django versions.
  116. return user is not None and user.is_active