diff --git a/CHANGELOG.md b/CHANGELOG.md index e733c3d00..62d1b1652 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * #651 Batch expired token deletions in `cleartokens` management command * Added pt-BR translations. * #729 Add support for [hashed client_secret values](https://django-oauth-toolkit.readthedocs.io/en/latest/settings.html#client-secret-hasher). +* #1077 Bug fix for hashed client secrets ### Fixed * #1012 Return status for introspecting a nonexistent token from 401 to the correct value of 200 per [RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662#section-2.2). diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 06ef64f09..96b7fbc21 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -129,7 +129,11 @@ def _authenticate_basic_auth(self, request): # used by our default generator does not include the "$" character. # However, if a different character set was used to generate the secret, this sentinel # might be a false positive. - elif "$" in request.client.client_secret and request.client.client_secret != client_secret: + elif ( + "$" in request.client.client_secret + and "$" not in client_secret + and request.client.client_secret != client_secret + ): if not check_password(client_secret, request.client.client_secret): log.debug("Failed basic auth: wrong hashed client secret %s" % client_secret) return False diff --git a/tests/test_client_credential.py b/tests/test_client_credential.py index 936cfb6ae..27df6b141 100644 --- a/tests/test_client_credential.py +++ b/tests/test_client_credential.py @@ -105,6 +105,11 @@ def test_client_credential_with_hashed_client_secret(self): response = self.client.post(reverse("oauth2_provider:token"), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 401) + # sending the hashed secret should return a 401 + auth_headers = get_basic_auth_header(self.application.client_id, self.application.client_secret) + response = self.client.post(reverse("oauth2_provider:token"), data=token_request_data, **auth_headers) + self.assertEqual(response.status_code, 401) + def test_client_credential_does_not_issue_refresh_token(self): token_request_data = { "grant_type": "client_credentials",