From 9790fcc0c9abe05e0a34194389dace6f97f0e5e5 Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Sun, 1 Feb 2026 14:30:56 +0330 Subject: [PATCH] Enhance PlugNMeetWebhookAPIView signature verification with debug logging - Updated the _verify_webhook_signature method to include detailed debug logging for better traceability of signature verification issues. - Improved error messages for missing headers and configuration settings. - Added checks for empty request bodies to assist in identifying middleware issues. - Adjusted logging to prevent sensitive information exposure while still providing useful debugging information. --- apps/course/views/webhook.py | 55 ++++++++++++++++++++++++++++++++---- config/settings/base.py | 6 ++-- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/apps/course/views/webhook.py b/apps/course/views/webhook.py index 68e3f64..3b72852 100644 --- a/apps/course/views/webhook.py +++ b/apps/course/views/webhook.py @@ -100,35 +100,78 @@ class PlugNMeetWebhookAPIView(APIView): status=status.HTTP_500_INTERNAL_SERVER_ERROR ) + # def _verify_webhook_signature(self, request) -> bool: + # """ + # Verify webhook signature using SHA256 HMAC. + # Expects Hash-Token header with SHA256 signature of request body. + # """ + # hash_token = request.headers.get('Hash-Token') + # if not hash_token: + # logger.warning(f"[PlugNMeet Webhook] Missing Hash-Token header") + # return False + + # # Get API secret from settings + # api_secret = getattr(settings, 'PLUGNMEET_API_SECRET', None) + # if not api_secret: + # logger.error(f"[PlugNMeet Webhook] PLUGNMEET_API_SECRET not configured") + # raise ImproperlyConfigured("PLUGNMEET_API_SECRET is not configured") + + # # Calculate expected signature + # body = request.body + # expected_signature = hmac.new( + # api_secret.encode('utf-8'), + # body, + # hashlib.sha256 + # ).hexdigest() + + # # Compare signatures (constant time comparison) + # is_valid = hmac.compare_digest(hash_token, expected_signature) + + # if not is_valid: + # logger.warning(f"[PlugNMeet Webhook] Signature mismatch - expected={expected_signature[:10]}... got={hash_token[:10]}...") + + # return is_valid + def _verify_webhook_signature(self, request) -> bool: """ - Verify webhook signature using SHA256 HMAC. - Expects Hash-Token header with SHA256 signature of request body. + DEBUG VERSION: Prints details to find the mismatch. """ hash_token = request.headers.get('Hash-Token') if not hash_token: - logger.warning(f"[PlugNMeet Webhook] Missing Hash-Token header") + logger.error("[PlugNMeet Webhook] MISSING Hash-Token header") return False # Get API secret from settings api_secret = getattr(settings, 'PLUGNMEET_API_SECRET', None) if not api_secret: - logger.error(f"[PlugNMeet Webhook] PLUGNMEET_API_SECRET not configured") + logger.error("[PlugNMeet Webhook] PLUGNMEET_API_SECRET is missing in settings.py") raise ImproperlyConfigured("PLUGNMEET_API_SECRET is not configured") + # DEBUG: Print the first/last few chars of the secret to ensure it's loaded correctly + # DO NOT log the whole secret in production! + logger.info(f"[DEBUG] Secret in Django: {api_secret[:4]}...{api_secret[-4:]}") + # Calculate expected signature body = request.body + + # DEBUG: Check if body is empty (common middleware issue) + if len(body) == 0: + logger.error("[DEBUG] Request Body is EMPTY! Middleware might have consumed it.") + expected_signature = hmac.new( api_secret.encode('utf-8'), body, hashlib.sha256 ).hexdigest() - # Compare signatures (constant time comparison) + # DEBUG: Compare them visually in logs + logger.info(f"[DEBUG] Received Token: {hash_token}") + logger.info(f"[DEBUG] Calculated: {expected_signature}") + is_valid = hmac.compare_digest(hash_token, expected_signature) if not is_valid: - logger.warning(f"[PlugNMeet Webhook] Signature mismatch - expected={expected_signature[:10]}... got={hash_token[:10]}...") + logger.error("[PlugNMeet Webhook] Signature MISMATCH") return is_valid diff --git a/config/settings/base.py b/config/settings/base.py index a8a7816..2b3ec1c 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -279,9 +279,9 @@ ADMIN_INDEX_TITLE = 'Imam Javad Administration' SITE_DOMAIN = "https://imamjavad.nwhco.ir" ONLINE_CLASS_FRONTEND_DOMAIN = env('ONLINE_CLASS_FRONTEND_DOMAIN', default=SITE_DOMAIN) ONLINE_CLASS_TOKEN_TTL = env.int('ONLINE_CLASS_TOKEN_TTL', default=3000) -PLUGNMEET_SERVER_URL = env('PLUGNMEET_SERVER_URL', default='https://meet.newhorizonco.uk') -PLUGNMEET_API_KEY = env('PLUGNMEET_API_KEY', default='habibmeet_api_key_2024') -PLUGNMEET_API_SECRET = env('PLUGNMEET_API_SECRET', default='habibmeet_secret_zumyyYWqv7KR2kUqvYdq4z4sXg7XTBD2ljT6_2024') +PLUGNMEET_SERVER_URL ='https://meet.newhorizonco.uk' +PLUGNMEET_API_KEY ='PLUGNMEET_API_KEY', default='habibmeet_api_key_2024' +PLUGNMEET_API_SECRET ='habibmeet_secret_zumyyYWqv7KR2kUqvYdq4z4sXg7XTBD2ljT6_2024' PLUGNMEET_TIMEOUT = env.float('PLUGNMEET_TIMEOUT', default=10.0)