Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0]

- **Breaking:** rename the verify result field `newIdTokenResponse` to `invalidTokenResponse`, matching the `exchangeUsingTokenExchange` and `adminGraphQLRequest` parameters. Update any code that reads this field:

```diff
- $result->newIdTokenResponse
+ $result->invalidTokenResponse
```

- Widen the `guzzlehttp/guzzle` constraint to `^7.0 || ^8.0` so installs on projects that pull Guzzle 8 (such as Laravel 13) resolve without error.

## [0.1.5]

- Verify the dest property is not a malicious URL before making a token exchange request
- Reject App Proxy requests with multiple `shop` query parameters with a 401 response.
- Refreshing a non-expiring token now returns a no-refresh-needed result instead of an error
- Checkout UI and Customer Account UI Extension requests now return `shop` without the `https://` prefix
- Update the README for the package

## [0.1.4]

Expand Down
431 changes: 288 additions & 143 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"require": {
"php": ">=8.2",
"firebase/php-jwt": "^7.0",
"guzzlehttp/guzzle": "^7.0"
"guzzlehttp/guzzle": "^7.0 || ^8.0"
},
"require-dev": {
"squizlabs/php_codesniffer": "^3.13"
Expand Down
18 changes: 9 additions & 9 deletions src/Internal/Verify/AdminUIExt.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'configuration_error',
detail: 'Expected request.method to be a non-empty string',
Expand All @@ -51,7 +51,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'configuration_error',
detail: 'Expected request.headers to be an object',
Expand All @@ -72,7 +72,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'configuration_error',
detail: 'Expected request.url to be a non-empty string',
Expand Down Expand Up @@ -103,7 +103,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'options_request',
detail: 'OPTIONS request handled for CORS preflight. Respond 204 No Content using the provided response.',
Expand All @@ -130,7 +130,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'missing_authorization_header',
detail: 'Required `Authorization` header is missing. Respond 401 Unauthorized using the provided response.',
Expand All @@ -152,7 +152,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'invalid_id_token',
detail: 'ID token verification failed. Respond 401 Unauthorized using the provided response.',
Expand Down Expand Up @@ -210,7 +210,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: $errorCode,
detail: $detailMsg,
Expand All @@ -234,7 +234,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'invalid_aud',
detail: 'ID token audience (aud) claim does not match clientId. Respond 401 Unauthorized using the provided response.',
Expand Down Expand Up @@ -266,7 +266,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
claims: $payload
),
userId: $userId,
newIdTokenResponse: [
invalidTokenResponse: [
'status' => 401,
'body' => '',
'headers' => [
Expand Down
28 changes: 14 additions & 14 deletions src/Internal/Verify/AppHomeReq.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private static function buildPatchIdTokenRedirect(array $urlParts, string $path,
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'redirect_to_patch_id_token_page',
detail: 'Embedded app without id_token. Redirect to the patch ID token page to obtain a new token using the provided response.',
Expand All @@ -86,7 +86,7 @@ public static function verify(array $req, array $config, mixed $appHomePatchIdTo
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'configuration_error',
detail: 'Expected appHomePatchIdTokenPath to be a non-empty string',
Expand All @@ -106,7 +106,7 @@ public static function verify(array $req, array $config, mixed $appHomePatchIdTo
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'configuration_error',
detail: "Expected appHomePatchIdTokenPath to be a non-empty string, but got ''",
Expand All @@ -128,7 +128,7 @@ public static function verify(array $req, array $config, mixed $appHomePatchIdTo
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'configuration_error',
detail: 'Expected request.url to be a non-empty string',
Expand All @@ -149,7 +149,7 @@ public static function verify(array $req, array $config, mixed $appHomePatchIdTo
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'configuration_error',
detail: 'Expected request.headers to be an object',
Expand Down Expand Up @@ -201,7 +201,7 @@ public static function verify(array $req, array $config, mixed $appHomePatchIdTo
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'invalid_id_token',
detail: 'ID token verification failed. Respond 401 Unauthorized using the provided response.',
Expand All @@ -225,7 +225,7 @@ public static function verify(array $req, array $config, mixed $appHomePatchIdTo
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'missing_authorization_and_id_token',
detail: 'Neither Authorization header nor id_token query parameter present. Respond 401 Unauthorized using the provided response.',
Expand Down Expand Up @@ -280,7 +280,7 @@ public static function verify(array $req, array $config, mixed $appHomePatchIdTo
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: $errorCode,
detail: $detailMsg,
Expand Down Expand Up @@ -312,7 +312,7 @@ public static function verify(array $req, array $config, mixed $appHomePatchIdTo
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'invalid_aud',
detail: 'ID token audience (aud) claim does not match clientId. Respond 401 Unauthorized using the provided response.',
Expand Down Expand Up @@ -343,8 +343,8 @@ public static function verify(array $req, array $config, mixed $appHomePatchIdTo
];
}

// Build newIdTokenResponse
$newIdTokenResponse = null;
// Build invalidTokenResponse
$invalidTokenResponse = null;
if (!$hasAuthorizationHeader) {
// Document request - build patch ID token URL
$cleanQuery = self::removeQueryParam($query, 'id_token');
Expand All @@ -355,7 +355,7 @@ public static function verify(array $req, array $config, mixed $appHomePatchIdTo

$patchIdTokenLocation = $urlParts['scheme'] . '://' . $urlParts['host'] . $appHomePatchIdTokenPath . '?' . $patchIdTokenQuery;

$newIdTokenResponse = [
$invalidTokenResponse = [
'status' => 302,
'body' => '',
'headers' => [
Expand All @@ -364,7 +364,7 @@ public static function verify(array $req, array $config, mixed $appHomePatchIdTo
];
} else {
// Fetch request
$newIdTokenResponse = [
$invalidTokenResponse = [
'status' => 401,
'body' => '',
'headers' => [
Expand All @@ -382,7 +382,7 @@ public static function verify(array $req, array $config, mixed $appHomePatchIdTo
claims: $payload
),
userId: $userId,
newIdTokenResponse: $newIdTokenResponse,
invalidTokenResponse: $invalidTokenResponse,
log: new LogWithReq(
code: 'verified',
detail: 'App Home request verified. Proceed with business logic.' . (!$hasAuthorizationHeader ? ' Include the headers in the provided response.' : ''),
Expand Down
2 changes: 1 addition & 1 deletion src/Internal/Verify/NonExchangeableIdToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ public static function verify(array $req, array $config, string $requestType): R

// Extract shop from dest claim
$dest = $payload['dest'] ?? '';
$shop = str_replace('.myshopify.com', '', $dest);
$shop = str_replace(['.myshopify.com', 'https://'], '', $dest);

return new ResultWithNonExchangeableIdToken(
ok: true,
Expand Down
18 changes: 9 additions & 9 deletions src/Internal/Verify/PosUIExt.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'configuration_error',
detail: 'Expected request.method to be a non-empty string',
Expand All @@ -51,7 +51,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'configuration_error',
detail: 'Expected request.headers to be an object',
Expand All @@ -72,7 +72,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'configuration_error',
detail: 'Expected request.url to be a non-empty string',
Expand Down Expand Up @@ -103,7 +103,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'options_request',
detail: 'OPTIONS request handled for CORS preflight. Respond 204 No Content using the provided response.',
Expand All @@ -130,7 +130,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'missing_authorization_header',
detail: 'Required `Authorization` header is missing. Respond 401 Unauthorized using the provided response.',
Expand All @@ -152,7 +152,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'invalid_id_token',
detail: 'ID token verification failed. Respond 401 Unauthorized using the provided response.',
Expand Down Expand Up @@ -206,7 +206,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: $errorCode,
detail: $detailMsg,
Expand All @@ -228,7 +228,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
shop: null,
idToken: null,
userId: null,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'invalid_aud',
detail: 'ID token audience (aud) claim does not match clientId. Respond 401 Unauthorized using the provided response.',
Expand Down Expand Up @@ -261,7 +261,7 @@ public static function verify(array $req, array $config): ResultWithExchangeable
claims: $payload
),
userId: $userId,
newIdTokenResponse: null,
invalidTokenResponse: null,
log: new LogWithReq(
code: 'verified',
detail: 'POS UI Extension request verified. Proceed with business logic.',
Expand Down
2 changes: 1 addition & 1 deletion src/Types/ResultWithExchangeableIdToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public function __construct(
public ?string $shop,
public ?IdToken $idToken,
public ?string $userId,
public ?array $newIdTokenResponse,
public ?array $invalidTokenResponse,
public LogWithReq $log,
public ResponseInfo $response,
) {
Expand Down
2 changes: 1 addition & 1 deletion src/Version.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

namespace Shopify\App;

const VERSION = '0.1.5';
const VERSION = '1.0.0';