Action required: existing public apps must use expiring offline access tokens by January 1, 2027

Hi,

Based on the edge cases that were discovered in another thread about this, I think that the following few changes regarding the expiring access tokens will be very welcome:

1.) Increase the duration of access token from 1 hour to 30 days. Lots of online services has this kind of duration for the token and as pointed by others, if an app is heavily using the Shopify API for backend changes, 1 hour expiration time can be problematic.

2.) If you request a new access token with the refresh token, sometimes, things can go wrong and you don’t receive the response with the new access and refresh token.
To cover this case, it would be good that Shopify API would return the same response (the same access and refresh token) as long as the request is made with the same original refresh token, for a month. This way, if a request fails, the app can just repeat it and it gets the same response again.

Also, as food for thought, some online services have their OAuth system set up in a way that once you request a new access token, the refresh token doesn’t get invalidated. Instead, the refresh token expiration date gets automatically extended by 90 days. This makes the whole OAuth process more resilient to network issues.

It would be great if these changes could be considered.

Thanks

I’ve run my first migrations and code changes across my apps to get ahead of the curve.

It’s all simple-ish enough, but god damn, hitting go button on the production migration script is a scary moment.

Fingers crossed everything is migrated and nothing is broken…

Good luck to everyone else! :saluting_face:

Spongebob's Plankton: Evil Laugh & Button Press

Same here. I’ve migrated 3 out of my 5 apps. The warnings have disappeared from the Dev Dashboard for those 3 apps, yet the email still reports hundreds of non-migrated tokens for them.

Is this expected? Should we ignore those numbers, or do they actually require any action?

Hi @TerenceShopify,

I’m migrating existing apps from Shopify’s legacy **non-expiring offline access tokens** to the new **expiring offline access token + refresh token** model.

My app has some existing merchants with **multiple markets** (GB, EU, US). Internally, I stored a separate JSON file for each market, although they all point to the same Shopify store and contain the same legacy offline access token.

Example:

myclient_gb.json{

“shop”: “myclient-store.myshopify.com”,

“accessToken”: “abcxyz”

}

myclient_eu.json{

“shop”: “myclient-store.myshopify.com”,

“accessToken”: “abcxyz”

}

myclient_us.json{

“shop”: “myclient-store.myshopify.com”,

“accessToken”: “abcxyz”

}

To migrate, I’m using the Token Exchange endpoint with the legacy offline access token:

$arrPostFields = [

'client_id'             => $clientId,

'client_secret'         => $clientSecret,

'grant_type'            => 'urn:ietf:params:oauth:grant-type:token-exchange',

'subject_token'         => $legacyOfflineAccessToken,

'subject_token_type'    => 'urn:shopify:params:oauth:token-type:offline-access-token',

'requested_token_type'  => 'urn:shopify:params:oauth:token-type:offline-access-token',

'expiring'              => '1'

];

Suppose I first migrate using the **GB** record. The request succeeds and I receive:

* a new expiring offline access token

* a refresh token

I save those values only in `myclient_gb.json`.

Later, my application processes `myclient_eu.json`, which still contains the original legacy non-expiring offline access token.

My questions are:

1. Can the same legacy non-expiring offline access token be exchanged multiple times to obtain new expiring tokens and refresh tokens?

2. Or does the first successful migration invalidate the legacy token?

3. If the legacy token becomes invalid after the first migration, what is the recommended way to migrate applications that have multiple records referencing the same store and the same legacy offline access token?

4. Should I identify records by the `shop` value and update every record with the newly returned expiring access token and refresh token after the first successful migration?

I’m looking for the recommended migration strategy for this scenario.

Thanks.

Starting January 1, 2027, public apps that make REST or GraphQL Admin API requests with non-expiring tokens receive error responses.

@TerenceShopify For installed shops that currently have a legacy non-expiring offline access token but remain frozen/closed until after January 1, 2027, the documentation does not address how migration will be handled.

The migration APIs require the current legacy non-expiring offline access token to perform the migration. Can you confirm whether migration from a legacy non-expiring offline access token to an expiring offline access token will continue to be supported after January 1, 2027, for existing installations that were not migrated before the deadline?

If not, what is the expected recovery flow for these merchants? Will they need to reauthorize or reinstall the app, or is there another supported migration path?

Hi @TerenceShopify ,

Title: What happens if a merchant disconnects the app during or immediately after refresh token rotation?

I’m trying to understand some edge cases with Shopify’s new expiring offline access token + refresh token model.

Suppose my app successfully exchanges a refresh token and Shopify returns a new access token and a new refresh token.

I’m wondering what happens in these scenarios:

Scenario 1

Shopify has already generated and returned the new refresh token, but before my application successfully stores it, the merchant uninstalls (disconnects) the app from the Shopify Admin.

  • Does the newly returned refresh token remain valid?

  • Or is it immediately revoked when the app is uninstalled?

Scenario 2

My application receives the response containing the new refresh token, but due to a server crash, network issue, or database failure, the new refresh token is never saved. Immediately afterward, the merchant uninstalls the app.

In this case:

  • My application only has the most recently received refresh token in memory (or perhaps only the previous one stored in the database).

  • After the app is reinstalled later, is there any way to recover from this situation?

  • Is the previously stored refresh token permanently invalid because refresh token rotation has already occurred?

  • Is reauthorization through the OAuth flow the only recovery option?

I’m trying to understand the expected behavior and the recommended way to handle these failure scenarios so that applications don’t permanently lose access due to an unexpected disconnect or server failure.

Thanks