Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/usage/utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ Accepted arguments:

This method will return whether the `hmac` key in `params` is valid.

## `validateSignature`

Determines if a request is valid by checking the HMAC hash received in a request.

Accepted arguments:
| Parameter | Type | Required | Default Value | Notes |
| --- | --- | :---: | :---: | --- |
| `params` | `array` | Yes | - | Query parameters from a URL |
| `secret` | `string` | Yes | - | The secret key associated with the app in the Partners Dashboard |

This method will return whether the `signature` key in `params` is valid.

## `decodeSessionToken`

Decodes the given session token (JWT) and extracts its payload, using `Context::$API_SECRET_KEY` as the secret.
Expand Down
25 changes: 25 additions & 0 deletions src/Utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,31 @@ public static function validateHmac(array $params, string $secret): bool
return hash_equals($hmac, $computedHmac);
}

/**
* Determines if request is valid by processing secret key through an HMAC-SHA256 hash function
*
* @param array $params array of parameters parsed from a URL
* @param string $secret the secret key associated with the app in the Partners Dashboard
*
* @return bool true if the generated hexdigest is equal to the signature parameter, false otherwise
*/
public static function validateSignature(array $params, string $secret): bool
{
$signature = $params['signature'] ?? '';
unset($params['signature']);

$params = array_map(
fn($k, $v) => "{$k}=" . (is_array($v) ? implode(',', $v) : $v),
array_keys($params),
array_values($params)
);
asort($params);

$computedSignature = hash_hmac('sha256', implode($params), $secret);;

return hash_equals($signature, $computedSignature);
}

/**
* Retrieves the query string arguments from a URL, if any
*
Expand Down
38 changes: 38 additions & 0 deletions tests/UtilsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,44 @@ public function testInvalidHmac()
));
}

public function testValidSignature()
{
// phpcs:ignore
$url = 'https://123456.ngrok.io?extra%5B%5D=1&extra%5B%5D=2&shop=some-shop.myshopify.com&path_prefix=%2Fapps%2Fawesome_reviews&timestamp=1337178173';
$params = Utils::getQueryParams($url);
$secret = 'test-secret';
$sortedParams = 'extra=1,2path_prefix=/apps/awesome_reviewsshop=some-shop.myshopify.comtimestamp=1337178173';
$params['signature'] = hash_hmac('sha256', $sortedParams, $secret);

$this->assertEquals(true, Utils::validateSignature(
$params,
$secret
));
}

public function testInvalidSignature()
{
// phpcs:ignore
$url = 'https://123456.ngrok.io?extra%5B%5D=1&extra%5B%5D=2&shop=some-shop.myshopify.com&path_prefix=%2Fapps%2Fawesome_reviews&timestamp=1337178173';
$params = Utils::getQueryParams($url);
$secret = 'test-secret';
$sortedParams = 'extra=1,2path_prefix=/apps/awesome_reviewsshop=some-shop.myshopify.comtimestamp=1337178173';
$params['signature'] = hash_hmac('sha256', $sortedParams, $secret);

// Check with a wrong secret
$this->assertEquals(false, Utils::validateHmac(
$params,
$secret . 'wrong'
));

// Check with the correct secret but with an altered request
$params['foo'] = 'bar';
$this->assertEquals(false, Utils::validateHmac(
$params,
$secret
));
}

public function testGetValidQueryParams()
{
$params = [
Expand Down