Skip to content

Commit 97a771c

Browse files
Refactor token generation to use secrets module (#9760)
* Refactor token generation to use secrets module * test: Add focused tests for Token.generate_key() method - Add test for valid token format (40 hex characters) - Add collision resistance test with 500 sample size - Add basic randomness quality validation - Ensure generated keys are unique and properly formatted
1 parent edc055d commit 97a771c

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

rest_framework/authtoken/models.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import binascii
2-
import os
1+
import secrets
32

43
from django.conf import settings
54
from django.db import models
@@ -34,7 +33,7 @@ def save(self, *args, **kwargs):
3433

3534
@classmethod
3635
def generate_key(cls):
37-
return binascii.hexlify(os.urandom(20)).decode()
36+
return secrets.token_hex(20)
3837

3938
def __str__(self):
4039
return self.key

tests/authentication/test_authentication.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ def put(self, request):
8181
@override_settings(ROOT_URLCONF=__name__)
8282
class BasicAuthTests(TestCase):
8383
"""Basic authentication"""
84+
8485
def setUp(self):
8586
self.csrf_client = APIClient(enforce_csrf_checks=True)
8687
self.username = 'john'
@@ -198,6 +199,7 @@ def test_decoding_of_utf8_credentials(self):
198199
@override_settings(ROOT_URLCONF=__name__)
199200
class SessionAuthTests(TestCase):
200201
"""User session authentication"""
202+
201203
def setUp(self):
202204
self.csrf_client = APIClient(enforce_csrf_checks=True)
203205
self.non_csrf_client = APIClient(enforce_csrf_checks=False)
@@ -418,6 +420,41 @@ def test_generate_key_accessible_as_classmethod(self):
418420
key = self.model.generate_key()
419421
assert isinstance(key, str)
420422

423+
def test_generate_key_returns_valid_format(self):
424+
"""Ensure generate_key returns a valid token format"""
425+
key = self.model.generate_key()
426+
assert len(key) == 40
427+
# Should contain only valid hexadecimal characters
428+
assert all(c in '0123456789abcdef' for c in key)
429+
430+
def test_generate_key_produces_unique_values(self):
431+
"""Ensure generate_key produces unique values across multiple calls"""
432+
keys = set()
433+
for _ in range(100):
434+
key = self.model.generate_key()
435+
assert key not in keys, f"Duplicate key generated: {key}"
436+
keys.add(key)
437+
438+
def test_generate_key_collision_resistance(self):
439+
"""Test collision resistance with reasonable sample size"""
440+
keys = set()
441+
for _ in range(500):
442+
key = self.model.generate_key()
443+
assert key not in keys, f"Collision found: {key}"
444+
keys.add(key)
445+
assert len(keys) == 500, f"Expected 500 unique keys, got {len(keys)}"
446+
447+
def test_generate_key_randomness_quality(self):
448+
"""Test basic randomness properties of generated keys"""
449+
keys = [self.model.generate_key() for _ in range(10)]
450+
# Consecutive keys should be different
451+
for i in range(len(keys) - 1):
452+
assert keys[i] != keys[i + 1], "Consecutive keys should be different"
453+
# Keys should not follow obvious patterns
454+
for key in keys:
455+
# Should not be all same character
456+
assert not all(c == key[0] for c in key), f"Key has all same characters: {key}"
457+
421458
def test_token_login_json(self):
422459
"""Ensure token login view using JSON POST works."""
423460
client = APIClient(enforce_csrf_checks=True)
@@ -480,6 +517,7 @@ def test_incorrect_credentials(self):
480517
authentication should run and error, even if no permissions
481518
are set on the view.
482519
"""
520+
483521
class IncorrectCredentialsAuth(BaseAuthentication):
484522
def authenticate(self, request):
485523
raise exceptions.AuthenticationFailed('Bad credentials')
@@ -571,6 +609,7 @@ def test_basic_authentication_raises_error_if_user_not_active(self):
571609

572610
class MockUser:
573611
is_active = False
612+
574613
old_authenticate = authentication.authenticate
575614
authentication.authenticate = lambda **kwargs: MockUser()
576615
try:

0 commit comments

Comments
 (0)