Update: more resilient refresh-token recovery for expiring offline tokens

We’ve shipped an update that makes refresh-token rotation more resilient when a refresh succeeds but an app doesn’t receive or persist the response.

We heard a recurring concern: a network interruption, worker failure, or database write failure could leave an app holding an older refresh token after Shopify had returned a new token pair. Once the former retry window passed, the app could be unable to refresh its offline token until a merchant opened the app again.

With this update, if an app still has the refresh token it used because the refreshed pair was lost or wasn’t saved, it can retry that older refresh token to recover a valid token pair. The older refresh token remains usable until the app begins using the newly issued refresh token.

There are deliberate limits:

  • The recovery period is up to 30 days from the original refresh token’s first use.
  • It doesn’t extend a refresh token beyond its normal 90-day lifetime.
  • Once an app uses the replacement refresh token, the prior token is retired.

Continue to treat each refresh response as an access-token/refresh-token pair: serialize refresh attempts for each shop, persist the returned pair atomically, and use the newest refresh token for the next refresh. This change provides a recovery path for lost responses; it doesn’t make long-expired or previously retired refresh tokens valid again.

For implementation guidance, see our docs on access token refresh for more details.

This is a great change and sounds like it should alleviate most of the concerns.

@TerenceShopify thank you. Now I think that because everyone who has a background running app is going to implement this. It would be helpful to share a diagram or implementation specification of the process: which values to save in the db (token, date..) and when, for how long…When to delete tokens. In case of a failure what to do, how to retry, and what happens in case of a retry if a running runtime already has a token that is refreshed mid-process, etc…

Not that it’s too complex, but basically we’re all going to implement the same thing, just tailored to our own infrastructure. We don’t need to get creative when implementing this, instead we need to implement it reliably because even a tiny mistake would have strong consequences.

If we just have to follow a blueprint it would help shipping with confidence and focus on the implementation details and not the design.

Thank you