|
| 1 | +import base64 |
| 2 | +import hashlib |
| 3 | +import hmac |
| 4 | + |
| 5 | + |
| 6 | +def verify_signature( |
| 7 | + *, |
| 8 | + request_body: str, |
| 9 | + signature_header: str, |
| 10 | + signature_key: str, |
| 11 | + notification_url: str, |
| 12 | +) -> bool: |
| 13 | + """ |
| 14 | + Verifies and validates an event notification. See the `documentation`_ for more details. |
| 15 | +
|
| 16 | + Args: |
| 17 | + request_body: The JSON body of the request. |
| 18 | + signature_header: The value for the `x-square-hmacsha256-signature` header. |
| 19 | + signature_key: The signature key from the Square Developer portal for the webhook subscription. |
| 20 | + notification_url: The notification endpoint URL as defined in the Square Developer portal for the webhook |
| 21 | + subscription. |
| 22 | +
|
| 23 | + Returns: |
| 24 | + bool: True if the signature is valid, indicating that the event can be trusted as it came from Square. |
| 25 | + False if the signature validation fails, indicating that the event did not come from Square, so it may |
| 26 | + be malicious and should be discarded. |
| 27 | +
|
| 28 | + Raises: |
| 29 | + ValueError: if `signature_key` or `notification_url` are empty. |
| 30 | +
|
| 31 | + .. _documentation: |
| 32 | + https://developer.squareup.com/docs/webhooks/step3validate |
| 33 | + """ |
| 34 | + if not request_body: |
| 35 | + return False |
| 36 | + if not signature_key: |
| 37 | + raise ValueError("signature_key is empty") |
| 38 | + if not notification_url: |
| 39 | + raise ValueError("notification_url is empty") |
| 40 | + |
| 41 | + # Perform UTF-8 encoding to bytes |
| 42 | + payload = notification_url + request_body |
| 43 | + payload_bytes = payload.encode("utf-8") |
| 44 | + signature_header_bytes = signature_header.encode("utf-8") |
| 45 | + signature_key_bytes = signature_key.encode("utf-8") |
| 46 | + |
| 47 | + # Compute the hash value |
| 48 | + hashing_obj = hmac.new( |
| 49 | + key=signature_key_bytes, msg=payload_bytes, digestmod=hashlib.sha256 |
| 50 | + ) |
| 51 | + hash_bytes = hashing_obj.digest() |
| 52 | + |
| 53 | + # Compare the computed hash vs the value in the signature header |
| 54 | + hash_base64 = base64.b64encode(hash_bytes) |
| 55 | + return hmac.compare_digest(hash_base64, signature_header_bytes) |
0 commit comments