From 99bc6cd9e8e31a8275544ac2d6bbaffb69ac82a2 Mon Sep 17 00:00:00 2001 From: Jeroen de Lau Date: Wed, 24 Jul 2024 10:53:12 +0200 Subject: [PATCH 1/2] modify SessionToken to work with from Checkout UI Extension --- src/Objects/Enums/SessionTokenSource.php | 31 ++++++++++++++ src/Objects/Values/SessionToken.php | 50 +++++++++++++++++++---- tests/Objects/Values/SessionTokenTest.php | 40 ++++++++++++++++++ 3 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 src/Objects/Enums/SessionTokenSource.php diff --git a/src/Objects/Enums/SessionTokenSource.php b/src/Objects/Enums/SessionTokenSource.php new file mode 100644 index 00000000..ce7539cf --- /dev/null +++ b/src/Objects/Enums/SessionTokenSource.php @@ -0,0 +1,31 @@ +parts = explode('.', $this->string); $body = json_decode(Util::base64UrlDecode($this->parts[1]), true); + $this->tokenSource = $this->determineTokenSource($body); + // Confirm token is not malformed Assert::thatAll([ - $body['iss'], $body['dest'], $body['aud'], - $body['sub'], $body['exp'], $body['nbf'], $body['iat'], $body['jti'], - $body['sid'], + ... $this->tokenSource === SessionTokenSource::APP + ? [ + $body['iss'], + $body['sub'], + $body['sid'], + ] + : [], ])->notNull(self::EXCEPTION_MALFORMED); // Format the values - $this->iss = $body['iss']; + $this->iss = $body['iss'] ?? ''; $this->dest = $body['dest']; $this->aud = $body['aud']; - $this->sub = $body['sub']; + $this->sub = $body['sub'] ?? ''; $this->jti = $body['jti']; - $this->sid = SessionId::fromNative($body['sid']); + $this->sid = SessionId::fromNative($body['sid'] ?? ''); $this->exp = new Carbon($body['exp']); $this->nbf = new Carbon($body['nbf']); $this->iat = new Carbon($body['iat']); // Parse the shop domain from the destination - $host = parse_url($body['dest'], PHP_URL_HOST); + $host = $this->findHost($body['dest']); $this->shopDomain = NullableShopDomain::fromNative($host); } @@ -357,7 +372,10 @@ protected function verifySignature(): void */ protected function verifyValidity(): void { - Assert::that($this->iss)->contains($this->dest, self::EXCEPTION_INVALID); + if($this->tokenSource === SessionTokenSource::APP) { + Assert::that($this->iss)->contains($this->dest, self::EXCEPTION_INVALID); + } + Assert::that($this->aud)->eq(Util::getShopifyConfig('api_key', $this->getShopDomain()), self::EXCEPTION_INVALID); } @@ -377,4 +395,20 @@ protected function verifyExpiration(): void $now->lessThan($this->getLeewayIssuedAt()), ])->false(self::EXCEPTION_EXPIRED); } + + protected function determineTokenSource(array $body): int + { + if(!isset($body['iss']) && !isset($body['sid'])) { + return SessionTokenSource::CHECKOUT_EXTENSION; + } + + return SessionTokenSource::APP; + } + + protected function findHost(string $destination): ?string + { + return Str::startsWith($destination, 'https') + ? parse_url($destination, PHP_URL_HOST) + : $destination; + } } diff --git a/tests/Objects/Values/SessionTokenTest.php b/tests/Objects/Values/SessionTokenTest.php index c3bbbda9..b852fbad 100644 --- a/tests/Objects/Values/SessionTokenTest.php +++ b/tests/Objects/Values/SessionTokenTest.php @@ -9,9 +9,49 @@ use Osiset\ShopifyApp\Contracts\Objects\Values\ShopDomain as ShopDomainValue; use Osiset\ShopifyApp\Objects\Values\SessionToken; use Osiset\ShopifyApp\Test\TestCase; +use Osiset\ShopifyApp\Util; class SessionTokenTest extends TestCase { + public function testShouldProcessForValidCheckoutExtensionToken(): void + { + $now = Carbon::now()->unix(); + $this->tokenDefaults = [ + 'dest' => 'shop-name.myshopify.com', + 'aud' => Util::getShopifyConfig('api_key'), + 'exp' => $now + 60, + 'nbf' => $now, + 'iat' => $now, + 'jti' => '00000000-0000-0000-0000-000000000000', + ]; + + $token = $this->buildToken(); + $st = SessionToken::fromNative($token); + + $this->assertInstanceOf(ShopDomainValue::class, $st->getShopDomain()); + $this->assertTrue(Str::contains($this->tokenDefaults['dest'], $st->getShopDomain()->toNative())); + + $this->assertInstanceOf(Carbon::class, $st->getExpiration()); + $this->assertSame($this->tokenDefaults['exp'], $st->getExpiration()->unix()); + + $this->assertInstanceOf(Carbon::class, $st->getIssuedAt()); + $this->assertSame($this->tokenDefaults['iat'], $st->getIssuedAt()->unix()); + + $this->assertInstanceOf(Carbon::class, $st->getNotBefore()); + $this->assertSame($this->tokenDefaults['nbf'], $st->getNotBefore()->unix()); + + $this->assertSame($this->tokenDefaults['dest'], $st->getDestination()); + $this->assertSame($this->tokenDefaults['aud'], $st->getAudience()); + $this->assertSame($this->tokenDefaults['jti'], $st->getTokenId()); + + $this->assertInstanceOf(SessionIdValue::class, $st->getSessionId()); + $this->assertSame('', $st->getSessionId()->toNative()); + + $this->assertSame('', $st->getIssuer()); + $this->assertSame('', $st->getSubject()); + + } + public function testShouldProcessForValidToken(): void { $token = $this->buildToken(); From aae249e999d5c3f440bc912717e019593662529f Mon Sep 17 00:00:00 2001 From: Jeroen de Lau Date: Thu, 12 Sep 2024 02:46:42 +0200 Subject: [PATCH 2/2] pass linter --- src/Objects/Values/SessionToken.php | 4 ++-- tests/Objects/Values/SessionTokenTest.php | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Objects/Values/SessionToken.php b/src/Objects/Values/SessionToken.php index b06bbfe0..7a85f514 100644 --- a/src/Objects/Values/SessionToken.php +++ b/src/Objects/Values/SessionToken.php @@ -372,7 +372,7 @@ protected function verifySignature(): void */ protected function verifyValidity(): void { - if($this->tokenSource === SessionTokenSource::APP) { + if ($this->tokenSource === SessionTokenSource::APP) { Assert::that($this->iss)->contains($this->dest, self::EXCEPTION_INVALID); } @@ -398,7 +398,7 @@ protected function verifyExpiration(): void protected function determineTokenSource(array $body): int { - if(!isset($body['iss']) && !isset($body['sid'])) { + if (!isset($body['iss']) && !isset($body['sid'])) { return SessionTokenSource::CHECKOUT_EXTENSION; } diff --git a/tests/Objects/Values/SessionTokenTest.php b/tests/Objects/Values/SessionTokenTest.php index b852fbad..ff3162fb 100644 --- a/tests/Objects/Values/SessionTokenTest.php +++ b/tests/Objects/Values/SessionTokenTest.php @@ -49,7 +49,6 @@ public function testShouldProcessForValidCheckoutExtensionToken(): void $this->assertSame('', $st->getIssuer()); $this->assertSame('', $st->getSubject()); - } public function testShouldProcessForValidToken(): void