This repository has been archived by the owner on Nov 30, 2022. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 378
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Work in Progress * Add new config option * Work in progress * Shopify token middleware * Remove redundant command * Removed debugging code * Revert some changes back to master versions * No longer need the JWT env option * Reverted newlines * Removed custom package * Expiration bugfix * Add root api route * Split routes * Authentication token tests * Nove logic * Update unit tests * Update unit tests * Reverted composer * Removed * Lint fix * Drop php 7.2 * Only use legacy factories when Laravel 8 is in use * Remove legacy package * Add docblock for helpers * Missing docblock * Style CI fixes * Style CI fixes * Force the middleware * Updated to throw exceptions instead of a plain response on token errors * Exception handler for bad tokens * Style CI fixes * trigger GitHub actions * Make sure the expiration in the test tokens is in the future
- Loading branch information
Showing
24 changed files
with
1,392 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
|
||
namespace Osiset\ShopifyApp\Exceptions; | ||
|
||
/** | ||
* Exception for use in requests that need http responses. | ||
*/ | ||
class HttpException extends BaseException | ||
{ | ||
public function render($request) | ||
{ | ||
if ($request->expectsJson()) { | ||
return response()->json([ | ||
'error' => $this->getMessage(), | ||
], $this->getCode()); | ||
} | ||
|
||
return response($this->getMessage(), $this->getCode()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<?php | ||
|
||
namespace Osiset\ShopifyApp\Http\Controllers; | ||
|
||
use Illuminate\Routing\Controller; | ||
use Osiset\ShopifyApp\Traits\ApiController as ApiControllerTrait; | ||
|
||
/** | ||
* Authenticates with a JWT through auth.token Middleware. | ||
*/ | ||
class ApiController extends Controller | ||
{ | ||
use ApiControllerTrait; | ||
|
||
/** | ||
* Create a new controller instance. | ||
* | ||
* @return void | ||
*/ | ||
public function __construct() | ||
{ | ||
$this->middleware('auth.token'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
<?php | ||
|
||
namespace Osiset\ShopifyApp\Http\Middleware; | ||
|
||
use Closure; | ||
use Illuminate\Http\Request; | ||
use function Osiset\ShopifyApp\base64url_decode; | ||
use function Osiset\ShopifyApp\base64url_encode; | ||
use Osiset\ShopifyApp\Exceptions\HttpException; | ||
use Osiset\ShopifyApp\Objects\Values\ShopDomain; | ||
use Osiset\ShopifyApp\Services\ShopSession; | ||
use Osiset\ShopifyApp\Traits\ConfigAccessible; | ||
|
||
class AuthToken | ||
{ | ||
use ConfigAccessible; | ||
|
||
/** | ||
* The shop session helper. | ||
* | ||
* @var ShopSession | ||
*/ | ||
protected $shopSession; | ||
|
||
/** | ||
* Constructor. | ||
* | ||
* @param ShopSession $shopSession The shop session helper. | ||
* | ||
* @return void | ||
*/ | ||
public function __construct(ShopSession $shopSession) | ||
{ | ||
$this->shopSession = $shopSession; | ||
} | ||
|
||
/** | ||
* Handle an incoming request. | ||
* | ||
* Get the bearer token, validate and verify, and create a | ||
* session based on the contents. | ||
* | ||
* The token is "url safe" (`+` is `-` and `/` is `_`) base64. | ||
* | ||
* @param Request $request The request object. | ||
* @param \Closure $next The next action. | ||
* | ||
* @return mixed | ||
*/ | ||
public function handle(Request $request, Closure $next) | ||
{ | ||
$now = time(); | ||
|
||
$token = $request->bearerToken(); | ||
|
||
if (! $token) { | ||
throw new HttpException('Missing authentication token', 401); | ||
} | ||
|
||
// The header is fixed so include it here | ||
if (! preg_match('/^eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.[A-Za-z0-9\-\_=]+\.[A-Za-z0-9\-\_\=]*$/', $token)) { | ||
throw new HttpException('Malformed token', 400); | ||
} | ||
|
||
if (! $this->checkSignature($token)) { | ||
throw new HttpException('Unable to verify signature', 400); | ||
} | ||
|
||
$parts = explode('.', $token); | ||
|
||
$body = base64url_decode($parts[1]); | ||
$signature = $parts[2]; | ||
|
||
$body = json_decode($body); | ||
|
||
if (! $body || | ||
! isset($body->iss) || | ||
! isset($body->dest) || | ||
! isset($body->aud) || | ||
! isset($body->sub) || | ||
! isset($body->exp) || | ||
! isset($body->nbf) || | ||
! isset($body->iat) || | ||
! isset($body->jti) || | ||
! isset($body->sid)) { | ||
throw new HttpException('Malformed token', 400); | ||
} | ||
|
||
if (($now > $body->exp) || ($now < $body->nbf) || ($now < $body->iat)) { | ||
throw new HttpException('Expired token', 403); | ||
} | ||
|
||
if (! stristr($body->iss, $body->dest)) { | ||
throw new HttpException('Invalid token', 400); | ||
} | ||
|
||
if ($body->aud !== $this->getConfig('api_key')) { | ||
throw new HttpException('Invalid token', 400); | ||
} | ||
|
||
// All is well, login | ||
$url = parse_url($body->dest); | ||
|
||
$this->shopSession->make(ShopDomain::fromNative($url['host'])); | ||
$this->shopSession->setSessionToken($body->sid); | ||
|
||
return $next($request); | ||
} | ||
|
||
/** | ||
* Checks the validity of the signature sent with the token. | ||
* | ||
* @param string $token The token to check. | ||
* | ||
* @return bool | ||
*/ | ||
private function checkSignature($token) | ||
{ | ||
$parts = explode('.', $token); | ||
$signature = array_pop($parts); | ||
$check = implode('.', $parts); | ||
|
||
$secret = $this->getConfig('api_secret'); | ||
$hmac = hash_hmac('sha256', $check, $secret, true); | ||
$encoded = base64url_encode($hmac); | ||
|
||
return $encoded === $signature; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<?php | ||
|
||
namespace Osiset\ShopifyApp\Traits; | ||
|
||
use Illuminate\Http\JsonResponse; | ||
use Illuminate\Support\Facades\Auth; | ||
use Osiset\ShopifyApp\Storage\Models\Plan; | ||
|
||
/** | ||
* Responsible for showing the main homescreen for the app. | ||
*/ | ||
trait ApiController | ||
{ | ||
/** | ||
* 200 Response. | ||
* | ||
* @return JsonResponse | ||
*/ | ||
public function index(): JsonResponse | ||
{ | ||
return response()->json(); | ||
} | ||
|
||
/** | ||
* Returns authenticated users details. | ||
* | ||
* @return JsonResponse | ||
*/ | ||
public function getSelf(): JsonResponse | ||
{ | ||
return response()->json(Auth::user()->only([ | ||
'name', | ||
'shopify_grandfathered', | ||
'shopify_freemium', | ||
'plan', | ||
])); | ||
} | ||
|
||
/** | ||
* Returns currently available plans. | ||
* | ||
* @return JsonResponse | ||
*/ | ||
public function getPlans(): JsonResponse | ||
{ | ||
return response()->json(Plan::all()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.