Description
ShopifyApp::exchangeUsingTokenExchange() always sends 'expiring' => 1 in the Token Exchange request body, regardless of the requested $accessMode. This causes offline tokens to expire after ~3600 seconds instead of being permanent.
Affected file
src/Internal/Exchange/TokenExchange.php
The request body is built with a hardcoded 'expiring' => 1 at the end, which applies to all access modes including offline.
Expected behavior
The Shopify Token Exchange API treats the expiring parameter differently depending on its value. When expiring=0 is sent (or the parameter is omitted), Shopify returns a non-expiring offline token with no expires_in and no refresh_token in the response. When expiring=1 is sent, Shopify returns a short-lived token with a ~3600s TTL and includes a refresh_token for later renewal.
When $accessMode is offline, the library should send expiring=0 to receive a permanent token. Sending expiring=1 silently opts the caller into short-lived tokens, which is only appropriate for online mode.
Actual behavior
All offline token exchange requests receive a token with ~3600s TTL. Any process that stores and reuses the offline token starts failing with 401 Unauthorized approximately one hour after the token was issued.
Links
https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens/token-exchange
https://shopify.dev/changelog/offline-access-tokens-now-support-expiry-and-refresh
Description
ShopifyApp::exchangeUsingTokenExchange()always sends'expiring' => 1in the Token Exchange request body, regardless of the requested$accessMode. This causes offline tokens to expire after ~3600 seconds instead of being permanent.Affected file
src/Internal/Exchange/TokenExchange.phpThe request body is built with a hardcoded
'expiring' => 1at the end, which applies to all access modes includingoffline.Expected behavior
The Shopify Token Exchange API treats the
expiringparameter differently depending on its value. Whenexpiring=0is sent (or the parameter is omitted), Shopify returns a non-expiring offline token with noexpires_inand norefresh_tokenin the response. Whenexpiring=1is sent, Shopify returns a short-lived token with a ~3600s TTL and includes arefresh_tokenfor later renewal.When
$accessModeisoffline, the library should sendexpiring=0to receive a permanent token. Sendingexpiring=1silently opts the caller into short-lived tokens, which is only appropriate foronlinemode.Actual behavior
All offline token exchange requests receive a token with ~3600s TTL. Any process that stores and reuses the offline token starts failing with
401 Unauthorizedapproximately one hour after the token was issued.Links
https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens/token-exchange
https://shopify.dev/changelog/offline-access-tokens-now-support-expiry-and-refresh