Verifying Shopify Webhooks for Public apps

Solved
shahrukhAhmed
Shopify Partner
23 0 2

Hi, 

I have been looking for documentation on how to verify webhooks for public apps and I am unable to find any. From, what I have gathered, private apps use a WEBHOOK_SIGNED_KEY available at the store admin level. I thought using API Key / API Secret could be an alternative for public apps, but that doesn't seem to be the case. The below code is what I have tried up till now. Thank you. 

def verify_hmac(secret, body, shopify_hmac):  
    hash_code = hmac.new(secret.encode('utf-8'), body, hashlib.sha256)  
    computed_hmac = base64.b64encode(hash_code.digest()).decode()
    return computed_hmac == shopify_hmac

@csrf_exempt
def save_webook_payload(request):
    if request.method == 'POST':

        shopify_hmac = request.headers.get('X-Shopify-Hmac-Sha256')  
        if verify_hmac(SHOPIFY_API_SECRET, request.body, shopify_hmac):  
            return JsonResponse( { 'data': 'Payload Recieved'}, safe = False )
        else:
            raise Http404("No such Page")

    raise Http404("No such Page")

 

0 Likes
Greg_Kujawa
Shopify Partner
937 79 201

This is an accepted solution.

If you refer to this link, there's a Ruby version of the routine to validate the HMAC signature sent with the webhook request --> https://shopify.dev/tutorials/manage-webhooks. You should be able to use that as a basis for your Python code. Hope this helps!

0 Likes
shahrukhAhmed
Shopify Partner
23 0 2

Thanks, still can't get computed_hmac and shopify_hmac to match. I have tried the app API secret key.  

0 Likes
syscon3
New Member
2 0 0

Hi, i have same issue, can you resolve it ?

Tks in advance.

0 Likes
shahrukhAhmed
Shopify Partner
23 0 2

Sure, let me know what your problem is. 

0 Likes
garyrgilbert
Excursionist
21 1 3

have you managed to get the computed_hmac and the shopify_hmac to match?

I've been looking for a solution to this problem as well without anyone giving me an answer

 

cheers,

 

Gary

0 Likes
Greg_Kujawa
Shopify Partner
937 79 201

I was able to in my projects. I can provide sample code for this, although it's in C#. Also have a JavaScript version somewhere around I can look for...

0 Likes
Greg_Kujawa
Shopify Partner
937 79 201

Here is sample code in C# that compares the provided HMAC against the calculated one. Works like a charm in production for our app proxy deployment.

var query = "path_prefix=" + path_prefix
                + "shop=" + shop
                + "timestamp=" + timestamp;

var secret_key = "MY_SECRET_KEY";
var ascii = new ASCIIEncoding();
var keyBytes = ascii.GetBytes(secret_key);
using (var hmac = new HMACSHA256(keyBytes))
{
	var queryBytes = ascii.GetBytes(query);
        var hashedQuery = hmac.ComputeHash(queryBytes);
        var computedHash = ToHexString(hashedQuery);
        if (computedHash == signature)
        {
		// Hooray!!!
	}
        else
        {
        	// Boo!!!
        }
}

private static string ToHexString(byte[] array)
{
	StringBuilder hex = new StringBuilder(array.Length * 2);
        foreach (byte b in array)
        {
        	hex.AppendFormat("{0:x2}", b);
	}
        
	return hex.ToString();
}
shahrukhAhmed
Shopify Partner
23 0 2

Hey Gary, 

Try this. Ignore some of the imports. 

 

carbon (1).png

0 Likes
Greg_Kujawa
Shopify Partner
937 79 201

Sorry, as my post was for verifying the app proxy calls, not the webhooks. This is directly from the Shopify GitHub example page. Ruby code...

def validate_hmac(hmac,request)
      h = request.params.reject{|k,_| k == 'hmac' || k == 'signature'}
      query = URI.escape(h.sort.collect{|k,v| "#{k}=#{v}"}.join('&'))
      digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), API_SECRET, query)

      unless (hmac == digest)
        return [403, "Authentication failed. Digest provided was: #{digest}"]
      end
    end

    def verify_webhook(hmac, data)
      digest = OpenSSL::Digest.new('sha256')
      calculated_hmac = Base64.encode64(OpenSSL::HMAC.digest(digest, API_SECRET, data)).strip

      hmac == calculated_hmac
    end
0 Likes