From 7ad3bf6acf74ff9446117468cdb15143f4bad936 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Wed, 16 Jan 2019 10:40:13 +0100 Subject: [PATCH] Added support for array keys (ids[]) --- lib/AuthHelper.php | 40 +++++++------ tests/AuthHelperTest.php | 117 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 18 deletions(-) create mode 100644 tests/AuthHelperTest.php diff --git a/lib/AuthHelper.php b/lib/AuthHelper.php index 3a6ff27..afab549 100644 --- a/lib/AuthHelper.php +++ b/lib/AuthHelper.php @@ -39,9 +39,11 @@ public static function getCurrentUrl() * * @return bool */ - public static function verifyShopifyRequest() + public static function verifyShopifyRequest($data = null) { - $data = $_GET; + if ($data === null) { + $data = $_GET; + } if(!isset(ShopifySDK::$config['SharedSecret'])) { throw new SdkException("Please provide SharedSecret while configuring the SDK client."); @@ -49,28 +51,30 @@ public static function verifyShopifyRequest() $sharedSecret = ShopifySDK::$config['SharedSecret']; - //Get the hmac and remove it from array - if (isset($data['hmac'])) { - $hmac = $data['hmac']; - unset($data['hmac']); - } else { + if (!isset($data['hmac'])) { throw new SdkException("HMAC value not found in url parameters."); } - //signature validation is deprecated - if (isset($data['signature'])) { - unset($data['signature']); - } - //Create data string for the remaining url parameters - $dataString = http_build_query($data); + + $hmac = $data['hmac']; + unset($data['hmac'], $data['signature']); + + $parts = array_map( function ($key, $param) { + // Map array values (ids[]=123&ids[]=456) to the correct format (ids=["123", "456"]). + // See: https://ecommerce.shopify.com/c/shopify-apis-and-technology/t/hmac-calculation-vs-ids-arrays-320575 + if (is_array($param)) { + $param = '["' . implode('", "', $param) . '"]'; + } + + return $key . '=' . $param; + }, array_keys($data), $data); + + ksort($parts); + $dataString = implode('&', $parts);; $realHmac = hash_hmac('sha256', $dataString, $sharedSecret); //hash the values before comparing (to prevent time attack) - if(md5($realHmac) === md5($hmac)) { - return true; - } else { - return false; - } + return md5($realHmac) === md5($hmac); } /** diff --git a/tests/AuthHelperTest.php b/tests/AuthHelperTest.php new file mode 100644 index 0000000..2699b08 --- /dev/null +++ b/tests/AuthHelperTest.php @@ -0,0 +1,117 @@ +verifyShopifyRequest(); + } catch (SdkException $exception) { + $this->assertEquals( + 'Please provide SharedSecret while configuring the SDK client.', + $exception->getMessage() + ); + + return; + } + + $this->fail('We expected an ' . SdkException::class . ' but got none'); + } + + public function testThrowsAnexceptionWhenTheHmacIsMissing() + { + ShopifySDK::config([ + 'ApiKey' => 'APIKEY', + 'SharedSecret' => 'SHAREDSECRET', + ]); + + $instance = new AuthHelper(); + + try { + $instance->verifyShopifyRequest(); + } catch (SdkException $exception) { + $this->assertEquals( + 'HMAC value not found in url parameters.', + $exception->getMessage() + ); + + return; + } + + $this->fail('We expected an ' . SdkException::class . ' but got none'); + } + + public function validatesTheHmacProvider() + { + return [ + 'regular format' => [ + [ + 'hmac' => 'eb963c62040fa312d35917aa6b17186261f78ded32cc088f0753ca9854c1b2fb', + 'locale' => 'en', + 'shop' => 'test-example.myshopify.com', + 'timestamp' => '1547629143', + ], + ], + 'with protocol=https://' => [ + [ + 'hmac' => '0a64939043800bb7cdca368a3458b06d3ac1ef7966b799551c58403d6cbe4ab9', + 'locale' => 'en', + 'shop' => 'test-example.myshopify.com', + 'protocol' => 'https://', + 'timestamp' => '1547629143', + ], + ], + 'with ids[] format' => [ + [ + 'hmac' => 'ec6e4f46ef4048410440757c3854cb842d358ea6e01caa78a5a641908178a7c8', + 'ids' => [ + '712036909111', + '709092507703', + ], + 'locale' => 'en', + 'shop' => 'test-example.myshopify.com', + 'timestamp' => '1547629143', + ], + ], + ]; + } + + /** + * @dataProvider validatesTheHmacProvider + */ + public function testValidatesTheHmac($paramters) + { + ShopifySDK::config([ + 'ApiKey' => 'APIKEY', + 'SharedSecret' => 'SHAREDSECRET', + ]); + + $instance = new AuthHelper(); + + $result = $instance->verifyShopifyRequest($paramters); + + $this->assertTrue($result); + } +} \ No newline at end of file