skip to Main Content

I am trying to verify the webhook received from Shopify but the Hmac verification fails.

def verify_webhook(data, hmac_header):
    digest = hmac.new(SECRET.encode('utf-8'), data, hashlib.sha256).digest()
    computed_hmac = base64.b64encode(digest)
    return hmac.compare_digest(computed_hmac, hmac_header.encode('utf-8'))


@app.route('/productCreation', methods=['POST'])
def productCreation():
    data = request.data
    verified = verify_webhook(
        data, request.headers.get('X-Shopify-Hmac-SHA256'))
    
    if(verified):
        return ("Success", 200)
    return("Integrity error", 401)

Getting error as

hash = hmac.new(SECRET.encode('utf-8'), body.encode('utf-8'), hashlib.sha256)
AttributeError: 'bytes' object has no attribute 'encode'

Can anyone help with this? I am developing a Flask app for this.

2

Answers


  1. You´re getting a attribute error, means your body object is not a string (encode() is working for string like objects) as mentioned in the error message it is a ‘bytes’ like object. Remove the
    .encode('utf-8') from body.

    Login or Signup to reply.
  2. Update March 2022

    It seems like Shopify has updated their Flask example with the same changes I suggested below.

    Original answer

    I got the same error when using Shopify’s Flask example

    from flask import Flask, request, abort
    import hmac
    import hashlib
    import base64
    
    app = Flask(__name__)
    
    SECRET = 'hush'
    
    def verify_webhook(data, hmac_header):
        digest = hmac.new(SECRET, data.encode('utf-8'), hashlib.sha256).digest()
        computed_hmac = base64.b64encode(digest)
    
        return hmac.compare_digest(computed_hmac, hmac_header.encode('utf-8'))
    
    @app.route('/webhook', methods=['POST'])
    def handle_webhook():
        data = request.get_data()
        verified = verify_webhook(data, request.headers.get('X-Shopify-Hmac-SHA256'))
    
        if not verified:
            abort(401)
    
        # process webhook payload
        # ...
    
        return ('', 200)
    

    To get it to work I had to modify verify_webhook by:

    • Encoding SECRET since hmac.new() expects the key in bytes and not as a string.
    • Not encoding data since Flask’s response.get_data already returns an encoded bytestring.

    The final code

    from flask import Flask, request, abort
    import hmac
    import hashlib
    import base64
    
    app = Flask(__name__)
    
    SECRET = 'hush'
    
    def verify_webhook(data, hmac_header):
        digest = hmac.new(SECRET.encode('utf-8'), data, hashlib.sha256).digest()
        computed_hmac = base64.b64encode(digest)
    
        return hmac.compare_digest(computed_hmac, hmac_header.encode('utf-8'))
    
    @app.route('/webhook', methods=['POST'])
    def handle_webhook():
        data = request.get_data()
        verified = verify_webhook(data, request.headers.get('X-Shopify-Hmac-SHA256'))
    
        if not verified:
            abort(401)
    
        # process webhook payload
        # ...
    
        return ('', 200)
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search