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

feature/cookieless #847

Merged
merged 94 commits into from Jun 25, 2021
Merged
Show file tree
Hide file tree
Changes from 93 commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
b2cecce
Work on new middleware for cookieless
gnikyt Apr 7, 2021
625a31b
Adjustments to check for HMAC on VerifyShopify
gnikyt Apr 7, 2021
0f27f4a
GET or POST value for token
gnikyt Apr 7, 2021
eaae8ef
Handle cases where no token and no HMAC are present
gnikyt Apr 7, 2021
27ac534
Return request
gnikyt Apr 7, 2021
f647b0e
More completion to middleware
gnikyt Apr 7, 2021
9d9ed70
Full redirect if malformed or invalid token
gnikyt Apr 7, 2021
7de729e
More completed work
gnikyt Apr 7, 2021
38a383b
Remove shop domain from verifySessionToken
gnikyt Apr 7, 2021
b7615f7
Utilize array helper for parse_url
gnikyt Apr 7, 2021
d7e6a54
Note
gnikyt Apr 7, 2021
1c2290d
Clean up of VerifyShopify middleware
gnikyt Apr 8, 2021
df80db0
More work on VerifyShopify.
gnikyt Apr 8, 2021
eacc1dc
Removal of ITP, cookie helper, shop session
gnikyt Apr 8, 2021
14d43cd
Removal of ITP, cookie helper, shop session
gnikyt Apr 8, 2021
69ac667
Added Polaris skeleton to token view
gnikyt Apr 8, 2021
77ab51e
Revised initial landing page
gnikyt Apr 8, 2021
e6e8971
Added session ID support
gnikyt Apr 21, 2021
4f1146c
Install flow modifications
gnikyt Apr 21, 2021
a058b4e
Support for other routes and token usage
gnikyt Apr 21, 2021
13e0a68
Auth flow and install flow refactoring
gnikyt Apr 21, 2021
c55d0e6
Updated SHOPIFY_API_REDRIECT to use /install instead of old /authenti…
gnikyt Apr 21, 2021
3d8e035
Revert authenticate route back to authenticate instead of 'install'
gnikyt Apr 21, 2021
5dc0053
Revert authenticate route back to authenticate instead of 'install'
gnikyt Apr 21, 2021
e5fe494
Install flow corrected
gnikyt Apr 21, 2021
4c08d1f
Blade support for token URLs with helper (tokenUrl)
gnikyt Apr 21, 2021
657707a
Added example Blade usage to tokenUrl helper
gnikyt Apr 21, 2021
43b3cc5
Tests updated for InstallShop action
gnikyt Apr 29, 2021
5642e0e
Updated test class naming for DeleteWebhooks action
gnikyt Apr 29, 2021
8ac298f
Started new tests for verify shop middleware
gnikyt Apr 29, 2021
f0febc8
Feature/cookieless - Billing, Turbo (#772)
May 5, 2021
e4ec542
Test cases for session token
gnikyt May 5, 2021
c7343c5
Test cases added for session context, verify shop middleware
gnikyt May 6, 2021
8bf8057
Check for "?" in URLs instead of "&" when determining the separator (…
squatto May 6, 2021
b445210
Fix test: use `authenticate.token` instead of `authenticate.oauth` (#…
squatto May 6, 2021
1f1f561
Use `contains()` to support route prefixes (#775)
squatto May 6, 2021
b30f14a
Use an env var for the new `turbo_enabled` config setting (#774)
squatto May 6, 2021
190e84e
Updated AuthProxy middleware
gnikyt May 6, 2021
32e567e
Update to BillingController and Billable middleware to remove old Sho…
gnikyt May 6, 2021
35fdf91
Modified tests to remove old ShopSession service
gnikyt May 6, 2021
2438146
Adjustments to API test cases
gnikyt May 6, 2021
385fb50
Modified existing tests to work with tokens
gnikyt May 6, 2021
4a8fcb2
Apply fixes from StyleCI (#778)
gnikyt May 6, 2021
849337d
Styling fixes
gnikyt May 6, 2021
ba1d469
Remove "token" from the query string of the target URL (#779)
squatto May 7, 2021
8a0bd17
Feature/cookieless - changes for turbolinks, install app (#780)
May 10, 2021
19a575d
Find shop domain in request when getting the token (#784)
squatto May 10, 2021
adc0bd7
Reflash the session when getting a token (to keep notices/errors, etc…
squatto May 10, 2021
ded50fb
Always pass the filtered query params to the token redirect (#785)
squatto May 12, 2021
a92e09e
ShopDomain::getFromRequest to ShopDomain::fromRequest to be consisten…
gnikyt May 12, 2021
5478d9f
Refactoring and additions for token routing.
gnikyt May 12, 2021
d00c3df
Fix to unit tests for token/ShopDomain/VerifyShop
gnikyt May 12, 2021
c9cfb12
StyleCI fixes
gnikyt May 12, 2021
7412a4d
StyleCI fixes
gnikyt May 12, 2021
4526fd7
Refactoring
gnikyt May 12, 2021
4953435
Move SessionContext to be a composite value object
gnikyt May 12, 2021
ac419a8
Minor cleanup of tests
gnikyt May 12, 2021
3ef152a
Switch to use constants for HTTP statuses
gnikyt May 12, 2021
fc2fc54
Fix for PHP <= 7.2
gnikyt May 12, 2021
2ad15ce
StyleCI fixes
gnikyt May 12, 2021
61f8aaf
Updates to handle Blade session tokens.
gnikyt May 13, 2021
2789c40
Update VerifyShopify.php (#808)
stevesweets Jun 21, 2021
d3df995
Update jQuery ajax header Authorization setting (#790)
thang12l Jun 22, 2021
de0d91d
Clean up on bearer token header settings
gnikyt Jun 23, 2021
2ade05e
Merge branch 'master' of github.com:osiset/laravel-shopify into featu…
gnikyt Jun 23, 2021
9bfa76a
StyleCI fix
gnikyt Jun 23, 2021
608dd99
Remove legacy factories package
gnikyt Jun 23, 2021
5319876
Billing flow adjusted to use tokens
gnikyt Jun 23, 2021
85b7d89
Working flow for billing by passing shop param initially
gnikyt Jun 23, 2021
b0055ff
Fix to unit tests for billing to pass shop domain to params
gnikyt Jun 23, 2021
9c6cc92
Update to token handler JS to setAttribute for inputs
gnikyt Jun 23, 2021
f94ecae
Merge branch 'master' of github.com:osiset/laravel-shopify into featu…
gnikyt Jun 23, 2021
ebf9635
Merge of 'master'.
gnikyt Jun 24, 2021
32f0d12
StyleCI fixes
gnikyt Jun 24, 2021
b3c4470
Merge branch 'master' of github.com:osiset/laravel-shopify into featu…
gnikyt Jun 24, 2021
bedf7e8
Fix to Kernel testcase referring to old middleware
gnikyt Jun 24, 2021
a4b3d88
Fix to undefined methods for macros
gnikyt Jun 24, 2021
a2b09ab
Fix to StyleCI suggestions
gnikyt Jun 24, 2021
0e78e2c
Remove build and bin folders
gnikyt Jun 24, 2021
a01e29f
Modified gitignore to exclude build folder from coverage and custom b…
gnikyt Jun 24, 2021
c9a3606
Remove and ignore build folder. (#848)
lucasmichot Jun 25, 2021
55bac2e
Unit tests for macros.
gnikyt Jun 25, 2021
6e3cf6c
Merge branch 'feature/cookieless' of github.com:osiset/laravel-shopif…
gnikyt Jun 25, 2021
1b6fd04
Added test for sessionToken directive
gnikyt Jun 25, 2021
96cc341
StyleCI fixes
gnikyt Jun 25, 2021
7be5eef
Remove trailing comma to prevent errors on tests with lower PHP versions
gnikyt Jun 25, 2021
06092a0
Uncomment interval on PlanDetails DTO
gnikyt Jun 25, 2021
5b06b14
Fix to SessionContext validity check for domain comparison
gnikyt Jun 25, 2021
edf3608
Resolve static method not found for tests on tokenRedirect and tokenR…
gnikyt Jun 25, 2021
8ea7ad0
StyleCI fixes
gnikyt Jun 25, 2021
f1f9ef6
PHPStan additions
gnikyt Jun 25, 2021
11fb2fd
Remove factory PHPStan error check
gnikyt Jun 25, 2021
83d9e79
Coverage for actions improved
gnikyt Jun 25, 2021
bb7e82c
Update src/ShopifyApp/Services/ChargeHelper.php
gnikyt Jun 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
@@ -1,3 +1,5 @@
/bin
/build
/vendor
composer.lock
.phpunit.result.cache
2 changes: 2 additions & 0 deletions phpstan.neon
Expand Up @@ -7,5 +7,7 @@ parameters:
- '#Access to an undefined property Osiset\\ShopifyApp\\Storage\\Models\\.*::\$.*\.#'
- '#Access to an undefined property Osiset\\ShopifyApp\\Test\\Stubs\\.*::\$.*\.#'
- '#Variable \$factory might not be defined\.#'
- '#Call to an undefined static method Illuminate\\Routing\\Redirector::tokenRedirect\(\).#'
- '#Call to an undefined static method Illuminate\\Routing\\UrlGenerator::tokenRoute\(\).#'
parallel:
maximumNumberOfProcesses: 2
1 change: 0 additions & 1 deletion phpunit.xml.dist
Expand Up @@ -23,7 +23,6 @@
<directory>src/ShopifyApp/Contracts/</directory>
<directory>src/ShopifyApp/Exceptions/</directory>
<directory>src/ShopifyApp/Objects/Enums/</directory>
<directory>src/ShopifyApp/Objects/Values/</directory>
<directory>src/ShopifyApp/resources/</directory>
<file>src/ShopifyApp/Messaging/Events/AppLoggedIn.php</file>
<file>src/ShopifyApp/ShopifyAppProvider.php</file>
Expand Down
12 changes: 5 additions & 7 deletions src/ShopifyApp/Actions/AfterAuthorize.php
Expand Up @@ -2,6 +2,7 @@

namespace Osiset\ShopifyApp\Actions;

use Illuminate\Support\Arr;
use Osiset\ShopifyApp\Contracts\Objects\Values\ShopId as ShopIdValue;
use Osiset\ShopifyApp\Contracts\Queries\Shop as IShopQuery;
use Osiset\ShopifyApp\Contracts\ShopModel as IShopModel;
Expand Down Expand Up @@ -50,8 +51,8 @@ public function __invoke(ShopIdValue $shopId): bool
* @return bool
*/
$fireJob = function (array $config, IShopModel $shop): bool {
$job = $config['job'];
if (isset($config['inline']) && $config['inline'] === true) {
$job = Arr::get($config, 'job');
if (Arr::get($config, 'inline', false)) {
// Run this job immediately
$job::dispatchNow($shop);
} else {
Expand All @@ -68,18 +69,15 @@ public function __invoke(ShopIdValue $shopId): bool

// Grab the jobs config
$jobsConfig = Util::getShopifyConfig('after_authenticate_job');

if (isset($jobsConfig[0])) {
if (Arr::has($jobsConfig, 0)) {
// We have multi-jobs
foreach ($jobsConfig as $jobConfig) {
// We have a job, pass the shop object to the contructor
$fireJob($jobConfig, $shop);
}

return true;
}

if (isset($jobsConfig['job'])) {
} elseif (Arr::has($jobsConfig, 'job')) {
// We have a single job
return $fireJob($jobsConfig, $shop);
}
Expand Down
44 changes: 17 additions & 27 deletions src/ShopifyApp/Actions/AuthenticateShop.php
Expand Up @@ -5,20 +5,12 @@
use Illuminate\Http\Request;
use Osiset\ShopifyApp\Contracts\ApiHelper as IApiHelper;
use Osiset\ShopifyApp\Objects\Values\ShopDomain;
use Osiset\ShopifyApp\Services\ShopSession;

/**
* Authenticates a shop and fires post authentication actions.
*/
class AuthenticateShop
{
/**
* The shop session handler.
*
* @var ShopSession
*/
protected $shopSession;

/**
* The API helper.
*
Expand All @@ -27,11 +19,11 @@ class AuthenticateShop
protected $apiHelper;

/**
* The action for authorizing a shop.
* The action for installing a shop.
*
* @var AuthorizeShop
* @var InstallShop
*/
protected $authorizeShopAction;
protected $installShopAction;

/**
* The action for dispatching scripts.
Expand All @@ -57,26 +49,23 @@ class AuthenticateShop
/**
* Setup.
*
* @param ShopSession $shopSession The shop session handler.
* @param IApiHelper $apiHelper The API helper.
* @param AuthorizeShop $authorizeShopAction The action for authorizing a shop.
* @param InstallShop $installShopAction The action for installing a shop.
* @param DispatchScripts $dispatchScriptsAction The action for dispatching scripts.
* @param DispatchWebhooks $dispatchWebhooksAction The action for dispatching webhooks.
* @param AfterAuthorize $afterAuthorizeAction The action for after authorize actions.
*
* @return void
*/
public function __construct(
ShopSession $shopSession,
IApiHelper $apiHelper,
AuthorizeShop $authorizeShopAction,
InstallShop $installShopAction,
DispatchScripts $dispatchScriptsAction,
DispatchWebhooks $dispatchWebhooksAction,
AfterAuthorize $afterAuthorizeAction
) {
$this->shopSession = $shopSession;
$this->apiHelper = $apiHelper;
$this->authorizeShopAction = $authorizeShopAction;
$this->installShopAction = $installShopAction;
$this->dispatchScriptsAction = $dispatchScriptsAction;
$this->dispatchWebhooksAction = $dispatchWebhooksAction;
$this->afterAuthorizeAction = $afterAuthorizeAction;
Expand All @@ -91,13 +80,15 @@ public function __construct(
*/
public function __invoke(Request $request): array
{
// Setup
$shopDomain = ShopDomain::fromNative($request->get('shop'));
$code = $request->get('code');

// Run the check
$result = call_user_func($this->authorizeShopAction, $shopDomain, $code);
if (! $result->completed) {
/** @var $result array */
$result = call_user_func(
$this->installShopAction,
ShopDomain::fromNative($request->get('shop')),
$request->query('code')
);

if (! $result['completed']) {
// No code, redirect to auth URL
return [$result, false];
}
Expand All @@ -110,10 +101,9 @@ public function __invoke(Request $request): array
}

// Fire the post processing jobs
$shopId = $this->shopSession->getShop()->getId();
call_user_func($this->dispatchScriptsAction, $shopId, false);
call_user_func($this->dispatchWebhooksAction, $shopId, false);
call_user_func($this->afterAuthorizeAction, $shopId);
call_user_func($this->dispatchScriptsAction, $result['shop_id'], false);
call_user_func($this->dispatchWebhooksAction, $result['shop_id'], false);
call_user_func($this->afterAuthorizeAction, $result['shop_id']);

return [$result, true];
}
Expand Down
Expand Up @@ -6,16 +6,15 @@
use Osiset\ShopifyApp\Contracts\Commands\Shop as IShopCommand;
use Osiset\ShopifyApp\Contracts\Queries\Shop as IShopQuery;
use Osiset\ShopifyApp\Objects\Enums\AuthMode;
use Osiset\ShopifyApp\Objects\Values\AccessToken;
use Osiset\ShopifyApp\Objects\Values\NullAccessToken;
use Osiset\ShopifyApp\Objects\Values\ShopDomain;
use Osiset\ShopifyApp\Services\ShopSession;
use Osiset\ShopifyApp\Util;
use stdClass;

/**
* Authenticates a shop via HTTP request.
* Install steps for a shop.
*/
class AuthorizeShop
class InstallShop
{
/**
* Querier for shops.
Expand All @@ -31,41 +30,30 @@ class AuthorizeShop
*/
protected $shopCommand;

/**
* The shop session handler.
*
* @var ShopSession
*/
protected $shopSession;

/**
* Setup.
*
* @param IShopQuery $shopQuery The querier for the shop.
* @param ShopSession $shopSession The shop session handler.
*
* @return void
*/
public function __construct(
IShopQuery $shopQuery,
IShopCommand $shopCommand,
ShopSession $shopSession
IShopCommand $shopCommand
) {
$this->shopQuery = $shopQuery;
$this->shopCommand = $shopCommand;
$this->shopSession = $shopSession;
}

/**
* Execution.
* TODO: Rethrow an API exception.
*
* @param ShopDomain $shopDomain The shop ID.
* @param string|null $code The code from Shopify.
*
* @return stdClass
* @return array
*/
public function __invoke(ShopDomain $shopDomain, ?string $code): stdClass
public function __invoke(ShopDomain $shopDomain, ?string $code): array
{
// Get the shop
$shop = $this->shopQuery->getByDomain($shopDomain, [], true);
Expand All @@ -75,42 +63,43 @@ public function __invoke(ShopDomain $shopDomain, ?string $code): stdClass
$shop = $this->shopQuery->getByDomain($shopDomain);
}

// Return data
$return = [
'completed' => false,
'url' => null,
];

$apiHelper = $shop->apiHelper();

// Access/grant mode
$apiHelper = $shop->apiHelper();
$grantMode = $shop->hasOfflineAccess() ?
AuthMode::fromNative(Util::getShopifyConfig('api_grant_mode', $shop)) :
AuthMode::OFFLINE();

$return['url'] = $apiHelper->buildAuthUrl($grantMode, Util::getShopifyConfig('api_scopes', $shop));

// If there's no code
if (empty($code)) {
return (object) $return;
}

// if the store has been deleted, restore the store to set the access token
if ($shop->trashed()) {
$shop->restore();
return [
'completed' => false,
'url' => $apiHelper->buildAuthUrl($grantMode, Util::getShopifyConfig('api_scopes', $shop)),
'shop_id' => $shop->getId(),
];
}

// We have a good code, get the access details
$this->shopSession->make($shop->getDomain());

try {
$this->shopSession->setAccess($apiHelper->getAccessData($code));
$return['url'] = null;
$return['completed'] = true;
// if the store has been deleted, restore the store to set the access token
if ($shop->trashed()) {
$shop->restore();
}

// Get the data and set the access token
$data = $apiHelper->getAccessData($code);
$this->shopCommand->setAccessToken($shop->getId(), AccessToken::fromNative($data['access_token']));

return [
'completed' => true,
'url' => null,
'shop_id' => $shop->getId(),
];
} catch (Exception $e) {
// Just return the default setting
return [
'completed' => false,
'url' => null,
'shop_id' => null,
];
}

return (object) $return;
}
}
12 changes: 12 additions & 0 deletions src/ShopifyApp/Contracts/Objects/Values/SessionId.php
@@ -0,0 +1,12 @@
<?php

namespace Osiset\ShopifyApp\Contracts\Objects\Values;

use Funeralzone\ValueObjects\ValueObject;

/**
* Session ID from session token.
*/
interface SessionId extends ValueObject
{
}
12 changes: 12 additions & 0 deletions src/ShopifyApp/Contracts/Objects/Values/SessionToken.php
@@ -0,0 +1,12 @@
<?php

namespace Osiset\ShopifyApp\Contracts\Objects\Values;

use Funeralzone\ValueObjects\ValueObject;

/**
* Session token.
*/
interface SessionToken extends ValueObject
{
}
19 changes: 18 additions & 1 deletion src/ShopifyApp/Contracts/ShopModel.php
Expand Up @@ -10,6 +10,7 @@
use Osiset\ShopifyApp\Contracts\Objects\Values\AccessToken as AccessTokenValue;
use Osiset\ShopifyApp\Contracts\Objects\Values\ShopDomain as ShopDomainValue;
use Osiset\ShopifyApp\Contracts\Objects\Values\ShopId as ShopIdValue;
use Osiset\ShopifyApp\Objects\Values\SessionContext;

/**
* Reprecents the shop model.
Expand All @@ -35,7 +36,7 @@ public function getDomain(): ShopDomainValue;
*
* @return AccessTokenValue
*/
public function getToken(): AccessTokenValue;
public function getAccessToken(): AccessTokenValue;

/**
* Gets charges belonging to the shop.
Expand Down Expand Up @@ -86,4 +87,20 @@ public function apiHelper(): IApiHelper;
* @return BasicShopifyAPI
*/
public function api(): BasicShopifyAPI;

/**
* Set the session context for the user.
*
* @param SessionContext $session The session context service.
*
* @return void
*/
public function setSessionContext(SessionContext $session): void;

/**
* Get the session context for the user.
*
* @return SessionContext|null
*/
public function getSessionContext(): ?SessionContext;
}
19 changes: 19 additions & 0 deletions src/ShopifyApp/Directives/SessionToken.php
@@ -0,0 +1,19 @@
<?php

namespace Osiset\ShopifyApp\Directives;

/**
* Provides a Blade directive for session tokens.
*/
class SessionToken
{
/**
* Output for the directive.
*
* @return string
*/
public function __invoke(): string
{
return '<input type="hidden" class="session-token" name="token" value="" />';
}
}
10 changes: 10 additions & 0 deletions src/ShopifyApp/Exceptions/MissingAuthUrlException.php
@@ -0,0 +1,10 @@
<?php

namespace Osiset\ShopifyApp\Exceptions;

/**
* Exception for handling a missing shop's myshopify domain.
*/
class MissingAuthUrlException extends BaseException
{
}