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

Commit

Permalink
feature/cookieless (#847)
Browse files Browse the repository at this point in the history
* Combined AuthShopify, AuthToken into new VerifyShopify middleware
* SessionToken value object created to verify and validate all aspects of the JWT
* New unauthenticated route and view added
* Removal of ITP, cookie helper, shop session as they're unneeded
* Added Polaris skeleton to token view
* Revised initial package landing page
* Added session ID support
* Moved AuthorizeShop to InstallShop
* Condensed InstallShop and modified to return an array
* Modified InstallShop to track access token update time
* Modified Shop command to track access token update time
* Support for other routes and token usage
* Removed authenticate.oauth route
* Removed oauthfailure method on authenticate controller
* Removed ShopSession class
* Updated shopify-config to reference new authenticate routes (install, token)
* Updated shopify-config to remove old authenticate routes
* Updated ShopModel's getToken to getAccessToken for naming conflict purposes
* Removed old ITP and authenticate routes from built-in route provider
* Updated SHOPIFY_API_REDRIECT to use /install instead of old /authenticate
* Revert authenticate route back to authenticate instead of 'install'
* Updated test class naming for DeleteWebhooks action
* Added TurboLink support
* Change name for test package
* Added getToken helper
* Added missing auth url exception
* Added billing to allowed routes, change token receipt
* Exception added to prevent loop redirects if authorization link is empty
* Added billing payments with tokens
* Removed unused classes
* Added test cases for session token
* Test cases added for session context, verify shop middleware
* Check for "?" in URLs instead of "&" when determining the separator (#777)
* Fix test: use `authenticate.token` instead of `authenticate.oauth` (#776)
* Use `contains()` to support route prefixes (#775)
* Use an env var for the new `turbo_enabled` config setting (#774)
* Updated code to use AuthManager from Laravel instead of auth()
* Updated response codes to use HTTP constants
* Update to BillingController and Billable middleware to remove old ShopSession service
* Remove "token" from the query string of the target URL (#779)
* Feature/cookieless - changes for turbolinks, install app  (#780)
* Redirect if the user clicked on any link before load Turbo
* Find shop domain in request when getting the token (#784)
* Add param to constructor to optionally not verify the token
* Add static method `ShopDomain::getFromRequest()` to find the shop domain
* Remove extraneous `ShopDomain` calls
* Always pass the filtered query params to the token redirect (#785)
* Added TokenRedirect macro for Laravel Redirect
* Added TokenRoute macro for Laravel URL/Route
* Updated ShopDomain::getFromRequest to be ShopDomain::fromRequest for consistency
* Moved HMAC and HMAC generation/comparison to value object (Hmac)
* Move SessionContext to be a composite value object
* Updated to handle Blade session tokens
* Added Blade directive "@sessionToken"
* Added support for jQuery, Turbolinks, and Axios for token bearer
* Added support for ".session-token" to automatically update with the token value
* Update jQuery ajax header Authorization setting (#790)
* Use template style setting for jQuery.ajaxSetup
* Use window.jQuery.ajaxSettings.headers = { } instead of ajaxSetup method
* Clean up on bearer token header settings
* Remove legacy factories package
* Billing flow adjusted to use tokens
* Fix to Kernel testcase referring to old middleware
* Fix to undefined methods for macros
* Remove build and bin folders from repo
* Added test for `tokenRoute`
* Added test for `tokenRedirect`
* Modified `TokenRedirect` and `TokenUrl` macros to use a common base class
* Added test for sessionToken directive
* Fix to SessionContext validity check for domain comparison
* Resolve static method not found for tests on tokenRedirect and tokenRoute

Co-authored-by: Lucas Michot <lucas@semalead.com>
Co-authored-by: Vitaly <32259003+Enmaboya@users.noreply.github.com>
Co-authored-by: Scott Carpenter <scott@payforstay.com>
Co-authored-by: Tyler King <osiset@users.noreply.github.com>
Co-authored-by: Vitaliy Dubov <vdubov@simtechdev.org>
Co-authored-by: Stephen Sweetland <steve.sweetland@gmail.com>
Co-authored-by: Tony Le <55417634+thang12l@users.noreply.github.com>
Co-authored-by: Lucas Michot <lucas@semalead.com>
  • Loading branch information
8 people authored Jun 25, 2021
1 parent 1971084 commit 4efa931
Show file tree
Hide file tree
Showing 92 changed files with 2,774 additions and 3,764 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/bin
/build
/vendor
composer.lock
.phpunit.result.cache
2 changes: 2 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Osiset\ShopifyApp\Exceptions;

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

0 comments on commit 4efa931

Please sign in to comment.