d
Topic
Posts:
19
Last edited 2 months ago

Verify that Embedded app settings page visit is from Shopify in PHP

When a client installs the app, they have the option to click on the app name in the list of apps on the `/admin/apps` page. 

When they click that page, my PHP index file for my app receives these `$_GET` vars:  

array(
    hmac => "d30c2n987643207m47fy72234570d2d640gfsd1c3883dr1s39871231b32b847cf",
    locale => "en",
    protocol => "https://",
    shop => "example-shop.myshopify.com",
    timestamp => "1535609063"
)

To verify a webhook from Shopify, I successfully use this:

    function verify_webhook($data, $hmac_header, $app_api_secret) {
        $calculated_hmac = base64_encode(hash_hmac('sha256', $data, $app_api_secret, true));
        return ($hmac_header == $calculated_hmac);
    }

    // Set vars for Shopify webhook verification
    $hmac_header = $_SERVER['HTTP_X_SHOPIFY_HMAC_SHA256'];
    $data = file_get_contents('php://input');
    $verified = verify_webhook($data, $hmac_header, MY_APP_API_SECRET);

    
Is it possible to verify an app admin page visit is from a Shopify client that has the app installed? 

PS: I've looked through both, the Embedded Apps API (but I can't figure out if that's even the right documentation or if I'm doing something wrong), as well as the GitHub example provided (which has no instructions on how to verify an Embedded App admin page visit).

i
Replies
Danilo Def Member
Posts:
11
3 months ago
g
1
upvotes

Hi Andre,

you maybe have some other one parameter in 

$data = file_get_contents('php://input');

try to check the dump.

And sometimes the problem come out because '&' symbol in url composition become translated in html and $calculated_hmac it will never be the same as the hmac.

 

I hope I have been helpful.

Best Regards.

Posts:
19
3 months ago
g
1
upvotes

Thank you for your help.  I've tried the `$data` route, but it's always empty when the client visits the app settings page, and seems to make no difference.

I've tried various other ways, discovering some ridiculous problems along the way, but still no luck.  

1.  The method I understand should be used to verify a Shopify HMAC is something akin to this:

        function verify_hmac($hmac = NULL, $shopify_app_api_secret) {
            $params_array = array();
            $hmac = $hmac ? $hmac : $_GET['hmac'];
            unset($_GET['hmac']);
            
            foreach($_GET as $key => $value){
                $key = str_replace("%","%25",$key);
                $key = str_replace("&","%26",$key);
                $key = str_replace("=","%3D",$key);
                $value = str_replace("%","%25",$value);
                $value = str_replace("&","%26",$value);
                $params_array[] = $key . "=" . $value;
            }
            
            $params_string = join('&', $params_array);
            $computed_hmac = hash_hmac('sha256', $params_string, $shopify_app_api_secret);
            
            return hash_equals($hmac, $computed_hmac);
        }

But the line `$params_string = join('&', $params_array);` causes an annoying problem by encoding `&timestamp` as `xtamp` ... Using `http_build_query($params_array)` results in the same ridiculous thing.  Found others having this same problem [here](https://stackoverflow.com/questions/45866266/%C3%97-multiplication-symbol-replaces-timestamp-in-url).  Basically resolved by encoding the `&` as `&`, to arrive at `$params_string = join('&', $params_array);`.

2.  My final version is like this, but still doesn't work (all the commented code is what else I've tried to no avail):  

        function verify_hmac($hmac = NULL, $shopify_app_api_secret) {
            $params_array = array();
            $hmac = $hmac ? $hmac : $_GET['hmac'];
            unset($_GET['hmac']);
        //     unset($_GET['protocol']);
        //     unset($_GET['locale']);
            
            foreach($_GET as $key => $value){
                $key = str_replace("%","%25",$key);
                $key = str_replace("&","%26",$key);
                $key = str_replace("=","%3D",$key);
                $value = str_replace("%","%25",$value);
                $value = str_replace("&","%26",$value);
                $params_array[] = $key . "=" . $value;
        //  This commented out method below was an attempt to see if 
        //  the imporperly encoded query param characters were causing issues
        /*
                if (!isset($params_string) || empty($params_string)) {
                    $params_string = $key . "=" . $value;
                }
                else {
                    $params_string = $params_string . "&" . $key . "=" . $value;
                }
        */
            }
            
        //  $params_string = join('&', $params_array);
        //     echo $params_string;
        //     $computed_hmac =  base64_encode(hash_hmac('sha256', $params_string, $shopify_app_api_secret, true));
        //  $computed_hmac =  base64_encode(hash_hmac('sha256', $params_string, $shopify_app_api_secret, false));
        //     $computed_hmac =  hash_hmac('sha256', $params_string, $shopify_app_api_secret, false);
        //     $computed_hmac =  hash_hmac('sha256', $params_string, $shopify_app_api_secret, true);
            $computed_hmac = hash_hmac('sha256', http_build_query($params_array), $shopify_app_api_secret);
            
            return hash_equals($hmac, $computed_hmac);
        }

I also have this question going at https://stackoverflow.com/questions/52090267/verify-that-embedded-app-settings-page-visit-is-from-shopify-in-php

And I still can't find any documentation on this besides what I've metnioned, which doesn't help.  Is there even a hmac verification when the client opens the app settings page in an embedded iframe?  Or am I just doing something wrong?

 

Danilo Def Member
Posts:
11
3 months ago

Hi Andre,

the code are correct but are you sure that you have all shopify parameter for generate correct $computed_hmac?

Control all parameter in $_GET, and try a dump of $_GET in settings page.

Be sure when you are in settings page all $_GET parameter begin "deleted" because the link to a settings is https://yourapp.com/settings without shopify parameter.

I know that the shopify parameters are passed only when the application is opened(Home page application). When the user navigato to application page/settings/other page shopify parameter begin unset cause the link don't report shopify parameter in link(https://yourapp.com/settings?code=1234&hmac=1234&shopurl=shopifyurl&timestamp=1234).

I hope I have been helpful.

Best Regards.

Posts:
19
Last edited 2 months ago

To be clear, the ap is already installed and functioning properly.

What I'm looking to authenticate is that client views of the `index.php` file in the Shopify app root folder, which is called when a client clicks on an installed app's name in their Installed Apps listing, are in deed coming from the client that installed the app.

That view is pictured here in the Shopify Docs for "Embedded App SDK":

Two application windows connected by a line labeled 'EASDK'

 

It might be that what I'm looking for is explained in Shopify Docs => Embedded Apps => OAuth, but I'm not sure I understand it, because implementing it as it is just redirects the user/client to the install prompt, which ends up being the update prompt, since tthe app is already installed.  And I can't even tell if this is relevant. 

The only `$_GET` params I receive to my `.php` script through Shopify's embedded view are exactly this:

array(
    hmac => "d30c2n987643207m47fy72234570d2d640gfsd1c3883dr1s39871231b32b847cf",
    locale => "en",
    protocol => "https://",
    shop => "example-shop.myshopify.com",
    timestamp => "1535609063"
)

 

I'm not even sure HMAC verification is what I need, but then how do I verify that the "shop" I receive in the $_GET parameter is actually the one sending requesting the page?

Danilo Def Member
Posts:
11
2 months ago

Hi Andre,

the parameter that you need for correct validation of "hmac" is "code" parameter always set in $_GET.

The "code" parameter is set in $_GET only when the app get installed.

 See here

For the error that you haved in the validation of "hmac" parameter in php you need to set htmlentities() while compiling the string for generate "hmac"

Example(extract of your code)

foreach($_GET as $key => $value){
     $key = str_replace("%","%25",$key);
     $key = str_replace("&","%26",$key);
     $key = str_replace("=","%3D",$key);
     $value = str_replace("%","%25",$value);
     $value = str_replace("&","%26",$value);
     $params_array[] = $key . "=" . $value;
}

$params_string = htmlentities(join('&',$params_array));

And then proceed to validate "hmac".

I hope I have been helpful.

Best Regards.

Posts:
19
about 2 months ago

Yeah, thank you. 
 

How do you validate if the code parameter is not set?

Danilo Def Member
Posts:
11
about 2 months ago

Hi Andre,

You are welcome.

You can validate the hmac only when the app begin installed in shop.

For your purpose you can try to set a webhook.

You can find webhook documentation in shopify docs.

 

Best Regards.

Posts:
19
about 2 months ago

Yep, got the webhooks. 

But now, how to verify Embedded App request?

Posts:
40
10 days ago

Hey Andre - just FYI I've spent a lot of time with this in PHP, and a combination of two things helped me out with my embedded app HMAC validation. 

I needed to use the code found in this tutorial: http://shaunagordon.com/blog/2017/08/23/shopify-hmac-verification/

But I also needed to wrap the 

http_build_query($request)

inside of a urldecode, like this:

urldecode(http_build_query($request))

Good luck!