Skip to content

shopify-app-express does not pass expiring: true during OAuth callback, causing 403 on all public apps created after April 1, 2026 #3234

Description

@ashishnitw

Overview

Package: @shopify/shopify-app-express@7.0.0
Related package: @shopify/shopify-api@13.0.0

Problem

As of April 1, 2026, Shopify requires expiring offline tokens for all new public apps
(changelog: https://shopify.dev/changelog/expiring-offline-access-tokens-required-for-new-public-apps-as-of-april-1-2026).

However, shopify-app-express never passes expiring: true when calling api.auth.callback()
in auth-callback.ts:

// packages/apps/shopify-app-express/src/auth/auth-callback.ts
const callbackResponse = await api.auth.callback({
rawRequest: req,
rawResponse: res,
// expiring is never passed — defaults to false
});

shopify-api@13 supports this via the expiring parameter (added in December 2025), but
shopify-app-express never uses it, so Shopify always returns a non-expiring shpat_ token.
Any API call made with that token on a new public app returns:

{ "networkStatusCode": 403, "message": "GraphQL Client: Forbidden" }

Expected behaviour

shopify-app-express should pass expiring: true by default for offline token OAuth flows,
since Shopify now mandates expiring tokens for all new public apps. Ideally this should also
be configurable (e.g. a useExpiringTokens option in shopifyApp() config) for apps that
need to opt out or migrate gradually.

Suggested fix

In auth-callback.ts, change:

const callbackResponse = await api.auth.callback({
rawRequest: req,
rawResponse: res,
});

to:

const callbackResponse = await api.auth.callback({
rawRequest: req,
rawResponse: res,
expiring: true,
});

Workaround

Skip shopify.auth.callback() and call shopify.api.auth.callback({ expiring: true })
directly in your own route handler, storing the session and registering webhooks manually.

Metadata

Metadata

Assignees

No one assigned

    Labels

    devtools-gardenerPost the issue or PR to Slack for the gardener

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions