Skip to content

Commit 628bbd5

Browse files
committed
TokenAuthentication: Allow custom keyword in the header
This allows subclassing TokenAuthentication and setting custom keyword, thus allowing the Authorization header to be for example: Bearer 956e252a-513c-48c5-92dd-bfddc364e812 It doesn't change the behavior of TokenAuthentication itself, it simply allows to reuse the logic of TokenAuthentication without the need of copy pasting the class and changing one hardcoded string. Related: #4080
1 parent 399e1c1 commit 628bbd5

File tree

2 files changed

+20
-7
lines changed

2 files changed

+20
-7
lines changed

rest_framework/authentication.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ class TokenAuthentication(BaseAuthentication):
150150
Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a
151151
"""
152152

153+
keyword = 'token'
153154
model = None
154155

155156
def get_model(self):
@@ -168,7 +169,7 @@ def get_model(self):
168169
def authenticate(self, request):
169170
auth = get_authorization_header(request).split()
170171

171-
if not auth or auth[0].lower() != b'token':
172+
if not auth or auth[0].lower() != self.keyword.encode():
172173
return None
173174

174175
if len(auth) == 1:
@@ -199,4 +200,4 @@ def authenticate_credentials(self, key):
199200
return (token.user, token)
200201

201202
def authenticate_header(self, request):
202-
return 'Token'
203+
return self.keyword.title()

tests/test_authentication.py

+17-5
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ class CustomTokenAuthentication(TokenAuthentication):
3535
model = CustomToken
3636

3737

38+
class CustomKeywordTokenAuthentication(TokenAuthentication):
39+
keyword = 'bearer'
40+
41+
3842
class MockView(APIView):
3943
permission_classes = (permissions.IsAuthenticated,)
4044

@@ -53,6 +57,7 @@ def put(self, request):
5357
url(r'^basic/$', MockView.as_view(authentication_classes=[BasicAuthentication])),
5458
url(r'^token/$', MockView.as_view(authentication_classes=[TokenAuthentication])),
5559
url(r'^customtoken/$', MockView.as_view(authentication_classes=[CustomTokenAuthentication])),
60+
url(r'^customkeywordtoken/$', MockView.as_view(authentication_classes=[CustomKeywordTokenAuthentication])),
5661
url(r'^auth-token/$', 'rest_framework.authtoken.views.obtain_auth_token'),
5762
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
5863
]
@@ -166,6 +171,7 @@ class BaseTokenAuthTests(object):
166171
urls = 'tests.test_authentication'
167172
model = None
168173
path = None
174+
header_prefix = 'Token '
169175

170176
def setUp(self):
171177
self.csrf_client = APIClient(enforce_csrf_checks=True)
@@ -179,31 +185,31 @@ def setUp(self):
179185

180186
def test_post_form_passing_token_auth(self):
181187
"""Ensure POSTing json over token auth with correct credentials passes and does not require CSRF"""
182-
auth = 'Token ' + self.key
188+
auth = self.header_prefix + self.key
183189
response = self.csrf_client.post(self.path, {'example': 'example'}, HTTP_AUTHORIZATION=auth)
184190
self.assertEqual(response.status_code, status.HTTP_200_OK)
185191

186192
def test_fail_post_form_passing_nonexistent_token_auth(self):
187193
# use a nonexistent token key
188-
auth = 'Token wxyz6789'
194+
auth = self.header_prefix + 'wxyz6789'
189195
response = self.csrf_client.post(self.path, {'example': 'example'}, HTTP_AUTHORIZATION=auth)
190196
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
191197

192198
def test_fail_post_form_passing_invalid_token_auth(self):
193199
# add an 'invalid' unicode character
194-
auth = 'Token ' + self.key + "¸"
200+
auth = self.header_prefix + self.key + "¸"
195201
response = self.csrf_client.post(self.path, {'example': 'example'}, HTTP_AUTHORIZATION=auth)
196202
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
197203

198204
def test_post_json_passing_token_auth(self):
199205
"""Ensure POSTing form over token auth with correct credentials passes and does not require CSRF"""
200-
auth = "Token " + self.key
206+
auth = self.header_prefix + self.key
201207
response = self.csrf_client.post(self.path, {'example': 'example'}, format='json', HTTP_AUTHORIZATION=auth)
202208
self.assertEqual(response.status_code, status.HTTP_200_OK)
203209

204210
def test_post_json_makes_one_db_query(self):
205211
"""Ensure that authenticating a user using a token performs only one DB query"""
206-
auth = "Token " + self.key
212+
auth = self.header_prefix + self.key
207213

208214
def func_to_test():
209215
return self.csrf_client.post(self.path, {'example': 'example'}, format='json', HTTP_AUTHORIZATION=auth)
@@ -273,6 +279,12 @@ class CustomTokenAuthTests(BaseTokenAuthTests, TestCase):
273279
path = '/customtoken/'
274280

275281

282+
class CustomKeywordTokenAuthTests(BaseTokenAuthTests, TestCase):
283+
model = Token
284+
path = '/customkeywordtoken/'
285+
header_prefix = 'Bearer '
286+
287+
276288
class IncorrectCredentialsTests(TestCase):
277289
def test_incorrect_credentials(self):
278290
"""

0 commit comments

Comments
 (0)