Skip to content

Support for specifying client secret hasher #1498

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ Ludwig Hähne
Łukasz Skarżyński
Madison Swain-Bowden
Marcus Sonestedt
Matej Spiller Muys
Matias Seniquiel
Michael Howitz
Owen Gong
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<!--
## [unreleased]
### Added
* Support for specifying client secret hasher via CLIENT_SECRET_HASHER setting.
### Changed
### Deprecated
### Removed
Expand Down
4 changes: 4 additions & 0 deletions docs/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ CLIENT_SECRET_GENERATOR_LENGTH
The length of the generated secrets, in characters. If this value is too low,
secrets may become subject to bruteforce guessing.

CLIENT_SECRET_HASHER
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The hasher for storing generated secrets. By default library will use the first hasher in PASSWORD_HASHERS.

EXTRA_SERVER_KWARGS
~~~~~~~~~~~~~~~~~~~
A dictionary to be passed to oauthlib's Server class. Three options
Expand Down
2 changes: 1 addition & 1 deletion oauth2_provider/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def pre_save(self, model_instance, add):
logger.debug(f"{model_instance}: {self.attname} is already hashed with {hasher}.")
except ValueError:
logger.debug(f"{model_instance}: {self.attname} is not hashed; hashing it now.")
hashed_secret = make_password(secret)
hashed_secret = make_password(secret, hasher=oauth2_settings.CLIENT_SECRET_HASHER)
setattr(model_instance, self.attname, hashed_secret)
return hashed_secret
return super().pre_save(model_instance, add)
Expand Down
1 change: 1 addition & 0 deletions oauth2_provider/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"CLIENT_ID_GENERATOR_CLASS": "oauth2_provider.generators.ClientIdGenerator",
"CLIENT_SECRET_GENERATOR_CLASS": "oauth2_provider.generators.ClientSecretGenerator",
"CLIENT_SECRET_GENERATOR_LENGTH": 128,
"CLIENT_SECRET_HASHER": "default",
"ACCESS_TOKEN_GENERATOR": None,
"REFRESH_TOKEN_GENERATOR": None,
"EXTRA_SERVER_KWARGS": {},
Expand Down
10 changes: 10 additions & 0 deletions tests/custom_hasher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.contrib.auth.hashers import PBKDF2PasswordHasher


class MyPBKDF2PasswordHasher(PBKDF2PasswordHasher):
"""
A subclass of PBKDF2PasswordHasher that uses less iterations.
"""

algorithm = "fast_pbkdf2"
iterations = 10000
2 changes: 2 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@
"tests",
)

PASSWORD_HASHERS = django.conf.settings.PASSWORD_HASHERS + ["tests.custom_hasher.MyPBKDF2PasswordHasher"]

LOGGING = {
"version": 1,
"disable_existing_loggers": False,
Expand Down
16 changes: 16 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,22 @@ def test_hashed_secret(self):
self.assertNotEqual(app.client_secret, CLEARTEXT_SECRET)
self.assertTrue(check_password(CLEARTEXT_SECRET, app.client_secret))

@override_settings(OAUTH2_PROVIDER={"CLIENT_SECRET_HASHER": "fast_pbkdf2"})
def test_hashed_from_settings(self):
app = Application.objects.create(
name="test_app",
redirect_uris="http://localhost http://example.com http://example.org",
user=self.user,
client_type=Application.CLIENT_CONFIDENTIAL,
authorization_grant_type=Application.GRANT_AUTHORIZATION_CODE,
client_secret=CLEARTEXT_SECRET,
hash_client_secret=True,
)

self.assertNotEqual(app.client_secret, CLEARTEXT_SECRET)
self.assertIn("fast_pbkdf2", app.client_secret)
self.assertTrue(check_password(CLEARTEXT_SECRET, app.client_secret))

def test_unhashed_secret(self):
app = Application.objects.create(
name="test_app",
Expand Down
Loading