From 9bb0136a1b765d53e6e9d9f51cbdda14a3dccc4a Mon Sep 17 00:00:00 2001 From: Cristiano de Paula Date: Tue, 26 Dec 2023 19:49:06 +0000 Subject: [PATCH 1/4] adding encrypt and decrypt methods --- oauth2_provider/models.py | 35 ++++++++++++++++++++++++++++ oauth2_provider/oauth2_validators.py | 20 +++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index ba42086bc..3edfcebbc 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -16,6 +16,7 @@ from .compat import parse_qsl, reverse, urlparse from .generators import generate_client_secret, generate_client_id from .validators import validate_uris +from cryptography.fernet import Fernet @python_2_unicode_compatible @@ -137,6 +138,40 @@ def is_usable(self, request): """ return True + @staticmethod + def encrypt_client_secret(client_secret): + key = settings.API_CREDENTIALS_KEY.encode() + # TODO: remove log below + print(" API_CREDENTIALS_KEY: {}".format(key)) + cipher_suite = Fernet(key) + encrypted_secret = cipher_suite.encrypt(client_secret.encode()) + return encrypted_secret + + @staticmethod + def decrypt_client_secret(encrypted_secret): + key = settings.API_CREDENTIALS_KEY.encode() + cipher_suite = Fernet(key) + decrypted_secret = cipher_suite.decrypt(encrypted_secret) + return decrypted_secret.decode() + + def save(self, *args, **kwargs): + if self.client_secret: + # I'ts necessary to check if client_secret was already encrypted + # try: + # hasher = identify_hasher(self.client_secret) + # return super(AbstractApplication, self).save(*args, **kwargs) + # except ValueError: + # TODO: remove log below + print("---"*30) + print(" client_secret: {}".format(self.client_secret)) + hashed_secret = AbstractApplication.encrypt_client_secret(self.client_secret) + # TODO: remove log below + print("---"*30) + print(" hashed_secret: {}".format(hashed_secret)) + self.client_secret = hashed_secret + + return super(AbstractApplication, self).save(*args, **kwargs) + class AppManager(models.Manager): def get_queryset(self): diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 5ed033a20..90a2fe370 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -50,6 +50,24 @@ def _extract_basic_auth(self, request): return auth_string + def _check_secret(self, provided_secret, stored_secret): + """ + Checks whether the provided client secret is valid. + Supports both hashed and unhashed secrets. + """ + try: + key = settings.API_CREDENTIALS_KEY.encode() + cipher_suite = Fernet(key) + decrypted_secret = cipher_suite.decrypt(stored_secret) + # TODO: remove log below + print("---"*30) + print(" decrypted_secret: {}".format(decrypted_secret)) + print("---"*30) + + return False if decrypted_secret != provided_secret else True + except ValueError: # Raised if the stored_secret is not hashed. + return constant_time_compare(provided_secret, stored_secret) + def _authenticate_basic_auth(self, request): """ Authenticates with HTTP Basic Auth. @@ -88,7 +106,7 @@ def _authenticate_basic_auth(self, request): elif request.client.client_id != client_id: log.debug("Failed basic auth: wrong client id %s" % client_id) return False - elif request.client.client_secret != client_secret: + elif not self._check_secret(client_secret, request.client.client_secret): log.debug("Failed basic auth: wrong client secret %s" % client_secret) return False else: From fe7cc16627bde141b83b24465ff1a528506536d8 Mon Sep 17 00:00:00 2001 From: Cristiano de Paula Date: Thu, 28 Dec 2023 18:28:59 +0000 Subject: [PATCH 2/4] removing encrypt and decrypt methods --- oauth2_provider/models.py | 35 ---------------------------- oauth2_provider/oauth2_validators.py | 20 +--------------- 2 files changed, 1 insertion(+), 54 deletions(-) diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index 3edfcebbc..ba42086bc 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -16,7 +16,6 @@ from .compat import parse_qsl, reverse, urlparse from .generators import generate_client_secret, generate_client_id from .validators import validate_uris -from cryptography.fernet import Fernet @python_2_unicode_compatible @@ -138,40 +137,6 @@ def is_usable(self, request): """ return True - @staticmethod - def encrypt_client_secret(client_secret): - key = settings.API_CREDENTIALS_KEY.encode() - # TODO: remove log below - print(" API_CREDENTIALS_KEY: {}".format(key)) - cipher_suite = Fernet(key) - encrypted_secret = cipher_suite.encrypt(client_secret.encode()) - return encrypted_secret - - @staticmethod - def decrypt_client_secret(encrypted_secret): - key = settings.API_CREDENTIALS_KEY.encode() - cipher_suite = Fernet(key) - decrypted_secret = cipher_suite.decrypt(encrypted_secret) - return decrypted_secret.decode() - - def save(self, *args, **kwargs): - if self.client_secret: - # I'ts necessary to check if client_secret was already encrypted - # try: - # hasher = identify_hasher(self.client_secret) - # return super(AbstractApplication, self).save(*args, **kwargs) - # except ValueError: - # TODO: remove log below - print("---"*30) - print(" client_secret: {}".format(self.client_secret)) - hashed_secret = AbstractApplication.encrypt_client_secret(self.client_secret) - # TODO: remove log below - print("---"*30) - print(" hashed_secret: {}".format(hashed_secret)) - self.client_secret = hashed_secret - - return super(AbstractApplication, self).save(*args, **kwargs) - class AppManager(models.Manager): def get_queryset(self): diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 90a2fe370..5ed033a20 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -50,24 +50,6 @@ def _extract_basic_auth(self, request): return auth_string - def _check_secret(self, provided_secret, stored_secret): - """ - Checks whether the provided client secret is valid. - Supports both hashed and unhashed secrets. - """ - try: - key = settings.API_CREDENTIALS_KEY.encode() - cipher_suite = Fernet(key) - decrypted_secret = cipher_suite.decrypt(stored_secret) - # TODO: remove log below - print("---"*30) - print(" decrypted_secret: {}".format(decrypted_secret)) - print("---"*30) - - return False if decrypted_secret != provided_secret else True - except ValueError: # Raised if the stored_secret is not hashed. - return constant_time_compare(provided_secret, stored_secret) - def _authenticate_basic_auth(self, request): """ Authenticates with HTTP Basic Auth. @@ -106,7 +88,7 @@ def _authenticate_basic_auth(self, request): elif request.client.client_id != client_id: log.debug("Failed basic auth: wrong client id %s" % client_id) return False - elif not self._check_secret(client_secret, request.client.client_secret): + elif request.client.client_secret != client_secret: log.debug("Failed basic auth: wrong client secret %s" % client_secret) return False else: From b92dde0315e615b7968cdfa5412b586e7917cdcc Mon Sep 17 00:00:00 2001 From: Cristiano de Paula Date: Thu, 28 Dec 2023 19:38:15 +0000 Subject: [PATCH 3/4] adding encrypt logic for client_secret on save method --- oauth2_provider/models.py | 19 +++++++++++++++++++ oauth2_provider/oauth2_validators.py | 15 ++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index ba42086bc..153d81633 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -4,6 +4,7 @@ from django.apps import apps from django.conf import settings +from django.contrib.auth.hashers import identify_hasher, make_password from django.db import models, transaction from django.utils import timezone @@ -137,6 +138,24 @@ def is_usable(self, request): """ return True + def save(self, *args, **kwargs): + if self.client_secret: + # I'ts necessary to check if client_secret was already encrypted + try: + hasher = identify_hasher(self.client_secret) + return super(AbstractApplication, self).save(*args, **kwargs) + except ValueError: + # TODO: remove log below + print("---"*30) + print(" client_secret: {}".format(self.client_secret)) + hashed_secret = make_password(self.client_secret) + # TODO: remove log below + print("---"*30) + print(" hashed_secret: {}".format(hashed_secret)) + self.client_secret = hashed_secret + + return super(AbstractApplication, self).save(*args, **kwargs) + class AppManager(models.Manager): def get_queryset(self): diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 5ed033a20..7686e8422 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -6,7 +6,9 @@ from datetime import timedelta from django.utils import timezone +from django.utils.crypto import constant_time_compare from django.conf import settings +from django.contrib.auth.hashers import check_password, identify_hasher from django.contrib.auth import authenticate from django.core.exceptions import ObjectDoesNotExist from django.db import transaction @@ -50,6 +52,17 @@ def _extract_basic_auth(self, request): return auth_string + def _check_secret(self, provided_secret, stored_secret): + """ + Checks whether the provided client secret is valid. + Supports both hashed and unhashed secrets. + """ + try: + identify_hasher(stored_secret) + return check_password(provided_secret, stored_secret) + except ValueError: # Raised if the stored_secret is not hashed. + return constant_time_compare(provided_secret, stored_secret) + def _authenticate_basic_auth(self, request): """ Authenticates with HTTP Basic Auth. @@ -88,7 +101,7 @@ def _authenticate_basic_auth(self, request): elif request.client.client_id != client_id: log.debug("Failed basic auth: wrong client id %s" % client_id) return False - elif request.client.client_secret != client_secret: + elif not self._check_secret(client_secret, request.client.client_secret): log.debug("Failed basic auth: wrong client secret %s" % client_secret) return False else: From 038603df4d8a9b040c662a31359ffafebec362a9 Mon Sep 17 00:00:00 2001 From: Cristiano de Paula Date: Tue, 2 Jan 2024 20:27:42 +0000 Subject: [PATCH 4/4] fixing validator rule --- oauth2_provider/oauth2_validators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 7686e8422..2f06eb12f 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -126,7 +126,7 @@ def _authenticate_request_body(self, request): if self._load_application(client_id, request) is None: log.debug("Failed body auth: Application %s does not exists" % client_id) return False - elif request.client.client_secret != client_secret: + elif not self._check_secret(client_secret, request.client.client_secret): log.debug("Failed body auth: wrong client secret %s" % client_secret) return False else: