Skip to content
This repository has been archived by the owner on Nov 30, 2022. It is now read-only.

bugfix/593 #732

Merged
merged 3 commits into from
Mar 10, 2021
Merged
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
111 changes: 79 additions & 32 deletions src/ShopifyApp/Http/Middleware/AuthShopify.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
use Closure;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Session;
use Osiset\ShopifyApp\Contracts\ApiHelper as IApiHelper;
Expand Down Expand Up @@ -68,7 +66,7 @@ public function __construct(IApiHelper $apiHelper, ShopSession $shopSession)
public function handle(Request $request, Closure $next)
{
// Grab the domain and check the HMAC (if present)
$domain = $this->getShopDomainFromData($request);
$domain = $this->getShopDomainFromRequest($request);
$hmac = $this->verifyHmac($request);

$checks = [];
Expand Down Expand Up @@ -230,6 +228,51 @@ private function getHmac(Request $request): ?array
return null;
}

/**
* Grab the shop, if present, and how it was found.
* Order of precedence is:.
*
* - GET/POST Variable
* - Headers
* - Referer
*
* @param Request $request The request object.
*
* @return ShopDomainValue
*/
private function getShopDomainFromRequest(Request $request): ShopDomainValue
{
// All possible methods
$options = [
// GET/POST
DataSource::INPUT()->toNative() => $request->input('shop'),
// Headers
DataSource::HEADER()->toNative() => $request->header('X-Shop-Domain'),
// Headers: Referer
DataSource::REFERER()->toNative() => function () use ($request): ?string {
$url = parse_url($request->header('referer'), PHP_URL_QUERY);
parse_str($url, $refererQueryParams);
if (! $refererQueryParams || ! isset($refererQueryParams['shop'])) {
return null;
}

return $refererQueryParams['shop'];
},
];

// Loop through each until we find the HMAC
foreach ($options as $method => $value) {
$result = is_callable($value) ? $value() : $value;
if ($result !== null) {
// Found a shop
return ShopDomain::fromNative($result);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

won't this just return whatever shop i inject into the http_referer variable?

}
}

// No shop domain found in any source
return NullShopDomain::fromNative(null);
}

/**
* Grab the data.
*
Expand All @@ -247,7 +290,7 @@ private function getData(Request $request, string $source): array
// Verify
$verify = [];
foreach ($request->query() as $key => $value) {
$verify[$key] = is_array($value) ? '["'.implode('", "', $value).'"]' : $value;
$verify[$key] = $this->parseDataSourceValue($value);
}

return $verify;
Expand All @@ -274,7 +317,7 @@ private function getData(Request $request, string $source): array

foreach (compact('code', 'locale', 'state', 'id', 'ids') as $key => $value) {
if ($value) {
$verify[$key] = is_array($value) ? '["'.implode('", "', $value).'"]' : $value;
$verify[$key] = $this->parseDataSourceValue($value);
}
}

Expand All @@ -288,7 +331,7 @@ private function getData(Request $request, string $source): array
// Verify
$verify = [];
foreach ($refererQueryParams as $key => $value) {
$verify[$key] = is_array($value) ? '["'.implode('", "', $value).'"]' : $value;
$verify[$key] = $this->parseDataSourceValue($value);
}

return $verify;
Expand All @@ -298,32 +341,6 @@ private function getData(Request $request, string $source): array
return $options[$source]();
}

/**
* Gets the shop domain from the data.
*
* @param Request $request The request object.
*
* @return ShopDomainValue
*/
private function getShopDomainFromData(Request $request): ShopDomainValue
{
$options = [
DataSource::INPUT()->toNative(),
DataSource::HEADER()->toNative(),
DataSource::REFERER()->toNative(),
];
foreach ($options as $option) {
$result = $this->getData($request, $option);
if (isset($result['shop'])) {
// Found a shop
return ShopDomain::fromNative($result['shop']);
}
}

// No shop domain found in any source
return NullShopDomain::fromNative(null);
}

/**
* Handle bad verification by killing the session and redirecting to auth.
*
Expand Down Expand Up @@ -353,4 +370,34 @@ private function handleBadVerification(Request $request, ShopDomainValue $domain
['shop' => $domain->toNative()]
);
}

/**
* Parse the data source value.
* Handle simple key/values, arrays, and nested arrays.
*
* @param mixed $value
*
* @return string
*/
private function parseDataSourceValue($value): string
{
/**
* Format the value.
*
* @param mixed $val
*
* @return string
*/
$formatValue = function ($val): string {
return is_array($val) ? '["'.implode('", "', $val).'"]' : $val;
};

// Nested array
if (is_array($value) && is_array(current($value))) {
return implode(', ', array_map($formatValue, $value));
}

// Array or basic value
return $formatValue($value);
}
}