Skip to content
CommerceGeek home

Security update: offline token dates and new app security features

Public apps calling the Admin API must move to expiring offline access tokens by 1 January 2027; Shopify also added client credentials access, IP allowlists and network access declarations.

A key tag, calendar page and hourglass showing an access token that now expires.
Published
1 Nov 2026
Affects
GraphQL Admin API, REST Admin API

What changed

On 27 October 2026 Shopify published a security update. It restates the enforcement dates for expiring offline access tokens and lists several new security features. It follows a 20 October post in which Shopify asked all app developers to secure their apps and said it would share more before any security change takes effect.

The offline token migration timeline has not changed. Shopify states that all public apps that call the Admin API must migrate to expiring offline access tokens. The dates are covered in a later section.

Shopify also says it rolled out these features that week:

  • Client credentials access: if your app loses a refresh token, your backend can request a new access token signed with your app's private key, without waiting for a merchant to reopen the app.

  • IP allowlist: you list the IP addresses your servers use, and Shopify rejects Admin API requests from any other IP, even with a valid access token.

  • Network access declarations: a new [network_access] section in shopify.app.toml where you list the servers your app's code loads from or sends requests to. Shopify uses the list to spot requests your app did not make. You can also list sites you link customers to, so those links skip the warning customers normally see before leaving the store.

  • Identity verification: Shopify now verifies your identity before installing apps on development and client-transfer stores.

  • App security hub: every app security practice now has its own page in the developer docs. Shopify suggests running shopify app security check before every release.

Who is affected

The offline token change applies to all public apps that call the Admin API. These apps must migrate to expiring offline access tokens by 1 January 2027.

Not affected:

  • Custom apps.

  • Merchant-created apps.

  • Online access tokens.

The migration guide says enforcement applies to GraphQL Admin API requests only. The changelog post refers to the Admin API more broadly, so check the guide if your app uses other Admin API surfaces.

The new security features are optional. The IP allowlist does not suit every app, though. It needs static, public IP addresses, and it does not work with the following:

  • Apps on platforms that assign dynamic IP addresses, such as most serverless platforms. Shopify says support for these is coming soon, but gives no date.

  • Access tokens used by the Shopify Flow Send HTTP request action, because Shopify cannot confirm the source IP address and rejects the requests.

  • Access tokens used for GraphQL Admin API requests from a storefront hosted on Oxygen, for the same reason.

Dev preview requests on dev stores, and direct API access requests from your app's embedded pages, are never rejected by the allowlist.

What to do: migrate to expiring offline tokens

There are two steps: start requesting expiring tokens, then migrate the installations that already hold non-expiring tokens. Find the route that matches how your app requests tokens today.

Step 1: Start requesting expiring tokens

If your app uses @shopify/shopify-app-remix or @shopify/shopify-app-react-router, add the expiringOfflineAccessTokens flag to your shopifyApp() configuration and redeploy with shopify app deploy. The template then stores and refreshes tokens for you.

Before

const shopify = shopifyApp({
  // ...
});

After

const shopify = shopifyApp({
  // ...
  future: {
    expiringOfflineAccessTokens: true,
  },
});

If your app implements token exchange or the authorization code grant itself, add expiring=1 to the request that obtains the token. For token exchange, the request is a POST to https://{shop}.myshopify.com/admin/oauth/access_token:

curl -X POST \
  https://{shop}.myshopify.com/admin/oauth/access_token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
  -d 'subject_token={id_token}' \
  -d 'subject_token_type=urn:ietf:params:oauth:token-type:id_token' \
  -d 'client_id={client_id}' \
  -d 'client_secret={client_secret}' \
  -d 'expiring=1'
Token exchange with expiring=1Unverified example.

For the authorization code grant, add the same expiring=1 parameter to the request that exchanges the code for an access token. In both cases, store the access_token and refresh_token from the response, along with expires_in and refresh_token_expires_in. Use the refresh token to get a new access token before expires_in seconds elapse, then store the refresh token that comes back and use that one next time.

Step 2: Migrate existing installations

Existing installations migrate when your app acquires and stores an expiring token pair. If you don't want to wait for users to open your app, or your app is background-only (webhook consumers, app proxy backends, Flow actions), use the direct migration request. It needs the stored non-expiring token and your client credentials, but no ID token or user session.

curl -X POST \
  https://{shop}.myshopify.com/admin/oauth/access_token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
  -d 'client_id={client_id}' \
  -d 'client_secret={client_secret}' \
  -d 'subject_token={non_expiring_offline_token}' \
  -d 'subject_token_type=urn:shopify:params:oauth:token-type:offline-access-token' \
  -d 'requested_token_type=urn:shopify:params:oauth:token-type:offline-access-token' \
  -d 'expiring=1'
Direct migration of a non-expiring offline tokenUnverified example.

The subject_token must be an active non-expiring offline access token that belongs to your app and store. Shopify rejects online tokens, delegate tokens, scope-restricted tokens and tokens that already expire. Both requested_token_type and expiring=1 are required. A request that breaks these rules returns 400 Bad Request with invalid_subject_token or invalid_requested_token_type.

If you lose the response, repeat the same request with the original token and valid client credentials within seven days of the initial exchange. Eligible retries return the same token pair. Shopify extends the access token's expiry when needed but not the refresh token's, so save the returned values. Recovery stops once your app refreshes the issued pair or a later token acquisition for the same store retires it. If a retry returns invalid_subject_token, acquire a new token through ID-token exchange or the authorization code grant.

Handle 403 and 401 errors

When Shopify rejects a GraphQL Admin API request that uses a non-expiring offline token, it returns 403 Forbidden. The errors value contains Non-expiring access tokens are no longer accepted for the Admin API, followed by a migration link. Match on that phrase rather than the whole string. A missing access scope also returns 403, so check the message before you re-authenticate.

  • Apps built from an app template: authenticate.admin(request) handles re-authentication automatically.

  • Apps using token exchange directly: catch the 403 and re-run token exchange to get a fresh token.

  • Apps using the authorization code grant: catch the 403, clear the stored token, and redirect the merchant through the OAuth flow to get a new access token.

  • Refresh token errors: if a refresh token is expired, already replaced by one your app has since used, or otherwise invalid, Shopify returns 401 Unauthorized with {"error": "invalid_request"}. Treat this as a signal to re-authenticate: the merchant must reopen your app in the Shopify admin to trigger a new token request.

The deadline and later dates

The migration deadline is 1 January 2027. It applies to all public apps that call the Admin API. The dates below apply to an app that has not migrated by then.

  • 1 January 2027: the app is delisted from the Shopify App Store and blocked from new installs. Existing installs keep working.

  • 15 January 2027: merchants using the app see a warning in Shopify admin.

  • 1 April 2027: Admin API requests that use non-expiring offline access tokens return HTTP 403.

The 27 October 2026 post is the date these dates were restated, not a deadline. The timeline itself is unchanged from the earlier announcement.

The sources give no deadline for the new optional security features.

New security features to consider

These features were announced on 27 October 2026. The sources state no deadline for any of them, and the announcement does not make them mandatory. Treat them as additional hardening, separate from the offline token migration.

Client credentials access

If your app loses a refresh token, your backend can request a new access token signed with your app's private key, without waiting for a merchant to reopen the app. It also suits background work no user is driving, such as scheduled syncs and webhook follow-up jobs.

  • Your server signs a short-lived JWT, called a client assertion, with a private key only your app holds. Shopify verifies it against a public key you add in the Dev Dashboard under Settings, in the Credentials section.

  • Shopify accepts RS256 (RSA, 2048 to 8192 bits) and ES256 (EC, P-256). The assertion's exp must be no more than 300 seconds after iat, and every jti can be used only once, so sign a new assertion for every request, including retries.

  • You send the assertion to the store's /admin/oauth/access_token endpoint with grant_type=client_credentials. Do not send your client secret in the same request.

  • For apps with public distribution, tokens are valid for up to one hour and the response contains no refresh token. Read expires_in rather than assuming a lifetime.

IP allowlist

With enforcement on, Shopify rejects GraphQL Admin API and REST Admin API requests from outside your allowed ranges with a 403, even if the access token is valid. Requests whose source address Shopify cannot confirm are also rejected.

  • Enter up to 100 addresses or CIDR ranges, one per line. Use /16 or narrower for IPv4 and /32 or narrower for IPv6, public addresses only, with each range starting at its network address.

  • In the Dev Dashboard, open Settings, then Manage in the IP allowlist section. Under Test against recent traffic, click Run test to see whether any of your own addresses from the last 30 days would be blocked.

  • Only then turn on Enforcement and save. Turning it off later keeps your addresses, so you can re-enable it.

Network access declarations

In the new [network_access] section of shopify.app.toml, list the servers your app's code loads from or sends requests to, wherever it runs on a store. Shopify uses the list to spot requests your app did not make. You can also list the sites you link customers to, so those links skip the warning customers normally see before leaving the store.

The sources do not give the key names or syntax within [network_access]. Check the app configuration documentation before writing it.

Reporting security issues

If you suspect a security issue with your app, notify Shopify immediately at partner-security-reports@shopify.com.

The app developer is accountable for fixing the problem and notifying affected merchants. Shopify says it will support you through the process.

For general security enquiries, contact Shopify Support (https://help.shopify.com/en/support). To ask questions about these updates, use the App Security board (https://community.shopify.dev/c/dev-platform/app-security/38) in the developer community.

Get the weekly digest

One email every Monday: what changed in Shopify, the deadlines coming up and what’s been announced, plus the week’s new guides. Free, and you can unsubscribe in one click.

Sources

  1. Security update: Offline token dates, new security features · Shopify · accessed 27 Oct 2026
  2. Migrate to expiring offline access tokens · Shopify developer docs · accessed 27 Oct 2026
  3. Get application-only access tokens for installed stores · Shopify developer docs · accessed 27 Oct 2026
  4. Restrict API access with an IP allowlist · Shopify developer docs · accessed 27 Oct 2026

AI-assisted, reviewed by Scott. Every claim is checked against the sources above.