Skip to content

Commit 03dbc3c

Browse files
committed
fix(iap): silence jwt token exceptions
1 parent 4228e76 commit 03dbc3c

File tree

5 files changed

+21
-27
lines changed

5 files changed

+21
-27
lines changed

src/Context/ContextResolver.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@
3636
*/
3737
class ContextResolver
3838
{
39-
public function __construct(private readonly InAppPurchaseProvider $inAppPurchaseProvider)
40-
{
39+
public function __construct(
40+
private readonly ?InAppPurchaseProvider $inAppPurchaseProvider = null
41+
) {
4142
}
4243

4344
/**
@@ -94,7 +95,7 @@ public function assembleModule(RequestInterface $request, ShopInterface $shop):
9495
if (!empty($params['in-app-purchases'])) {
9596
/** @var non-empty-string $inAppPurchaseString */
9697
$inAppPurchaseString = $params['in-app-purchases'];
97-
$inAppPurchases = $this->inAppPurchaseProvider->decodePurchases($inAppPurchaseString, $shop);
98+
$inAppPurchases = $this->inAppPurchaseProvider?->decodePurchases($inAppPurchaseString, $shop);
9899
}
99100

100101
return new ModuleAction(
@@ -259,7 +260,7 @@ public function assembleStorefrontRequest(RequestInterface $request, ShopInterfa
259260
throw new MalformedWebhookBodyException();
260261
}
261262

262-
$inAppPurchases = $this->inAppPurchaseProvider->decodePurchases($claims['inAppPurchases'], $shop);
263+
$inAppPurchases = $this->inAppPurchaseProvider?->decodePurchases($claims['inAppPurchases'], $shop);
263264
}
264265

265266
return new StorefrontAction(
@@ -322,7 +323,7 @@ private function parseSource(array $source, ShopInterface $shop): ActionSource
322323
throw new MalformedWebhookBodyException();
323324
}
324325

325-
$inAppPurchases = $this->inAppPurchaseProvider->decodePurchases($source['inAppPurchases'], $shop);
326+
$inAppPurchases = $this->inAppPurchaseProvider?->decodePurchases($source['inAppPurchases'], $shop);
326327
}
327328

328329

src/Context/InAppPurchase/HasMatchingDomain.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
/**
1414
* @phpstan-import-type InAppPurchaseArray from InAppPurchaseProvider
15+
*
16+
* @deprecated Will be removed with version 5.0.0, as no licence domain can be provided for validation
1517
*/
1618
class HasMatchingDomain implements Constraint
1719
{

src/Context/InAppPurchase/HasValidRSAJWKSignature.php

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@
1111
use Lcobucci\JWT\Signer\Rsa\Sha384;
1212
use Lcobucci\JWT\Signer\Rsa\Sha512;
1313
use Lcobucci\JWT\Token;
14-
use Lcobucci\JWT\UnencryptedToken;
1514
use Lcobucci\JWT\Validation\Constraint;
16-
use Lcobucci\JWT\Validation\ConstraintViolation;
15+
use Lcobucci\JWT\Validation\Constraint\SignedWith;
1716
use Strobotti\JWK\Key\KeyInterface;
1817
use Strobotti\JWK\Key\Rsa as RsaKey;
1918
use Strobotti\JWK\KeyConverter;
@@ -27,27 +26,21 @@ public function __construct(private readonly KeySet $keys)
2726
{
2827
}
2928

29+
/**
30+
* {@inheritDoc}
31+
*/
3032
public function assert(Token $token): void
3133
{
32-
if (!$token instanceof UnencryptedToken) {
33-
throw new \Exception('Token must be a plain JWT');
34-
}
35-
3634
$this->validateAlgorithm($token);
3735

3836
$key = $this->getValidKey($token);
3937

4038
/** @var non-empty-string $pem */
4139
$pem = $this->convertToPem($key);
4240

43-
/** @var string $alg */
44-
$alg = $token->headers()->get('alg');
45-
46-
$signer = $this->getSigner($alg);
41+
$signer = $this->getSigner($token->headers()->get('alg'));
4742

48-
if (!$signer->verify($token->signature()->hash(), $token->payload(), InMemory::plainText($pem))) {
49-
throw ConstraintViolation::error('Token signature mismatch', $this);
50-
}
43+
(new SignedWith($signer, InMemory::plainText($pem)))->assert($token);
5144
}
5245

5346
private function validateAlgorithm(Token $token): void

src/Context/InAppPurchase/InAppPurchaseProvider.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use Shopware\App\SDK\Shop\ShopInterface;
1515

1616
/**
17-
* @phpstan-type InAppPurchaseArray=array{identifier: string, quantity: int, nextBookingDate?: string, sub: string}
17+
* @phpstan-type InAppPurchaseArray array{identifier: string, quantity: int, nextBookingDate?: string, sub: string}
1818
*/
1919
class InAppPurchaseProvider
2020
{
@@ -27,21 +27,19 @@ public function __construct(
2727
/**
2828
* @param non-empty-string $encodedPurchases
2929
* @return Collection<InAppPurchase>
30-
* @throws \Exception
3130
*/
3231
public function decodePurchases(string $encodedPurchases, ShopInterface $shop, bool $retried = false): Collection
3332
{
3433
try {
3534
$keys = $this->keyFetcher->getKey($retried);
3635
$signatureValidator = new HasValidRSAJWKSignature($keys);
37-
$domainValidator = new HasMatchingDomain($shop);
3836

3937
$parser = new Parser(new JoseEncoder());
4038
/** @var Token\Plain $token */
4139
$token = $parser->parse($encodedPurchases);
4240

4341
$validator = new Validator();
44-
$validator->assert($token, $signatureValidator, $domainValidator);
42+
$validator->assert($token, $signatureValidator);
4543

4644
return $this->transformClaims($token);
4745
} catch (\Exception $e) {
@@ -50,8 +48,8 @@ public function decodePurchases(string $encodedPurchases, ShopInterface $shop, b
5048
}
5149

5250
$this->logger->error('Failed to decode in-app purchases: ' . $e->getMessage());
53-
54-
throw $e;
51+
52+
return new Collection();
5553
}
5654
}
5755

tests/Context/InAppPurchase/InAppPurchaseProviderTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ public function testDecodePurchaseRetryFails(): void
122122
(new KeySetFactory())->createFromJSON('{"keys": [{"kty": "RSA","n": "yHenasOsOl-Vv2BmpayS1R8l5L-JN99FwaRRKXFssGTjJDwbYdbe3CqTSKqtOfdqZLzE6-bN2-Q1xqZZsgs0_zHNx7EROXNG_uQs1uuGkS6bgGhnq_2d7wzFvCsyI00CDXZxRlGjKAEhvcXormomF1jpUW08Y5tPeUvMSdEZbZxW1ydir-UrMm1RUSgJgSP-sUqLG7kTIJ6SG7cLtF8c8cHcVXFljMyiYLQHYOECj1oklwvfrfaoT3OKdKGumi39rDthXtFa0Aq1OS_P9qfZJ-yXiQlpf2RxRr3Q5EQJ8E9iqrlOndbkSq7eXne2DvvgsiNdyzRWFvxWSPSd9GZXkw","e": "AQAB","kid": "-1xljHNcPM59Qx9OcULA9LS219bsmKCZueVXhdF0N0k","use": "sig","alg": "RS256"}]}'),
123123
);
124124

125-
static::expectException(RequiredConstraintsViolated::class);
126-
127125
$provider = new InAppPurchaseProvider($fetcher, $logger);
128-
$provider->decodePurchases($token->toString(), $shop, true);
126+
$decoded = $provider->decodePurchases($token->toString(), $shop, true);
127+
128+
static::assertEmpty($decoded);
129129
}
130130
}

0 commit comments

Comments
 (0)